summaryrefslogtreecommitdiff
path: root/ext/json
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json')
-rw-r--r--ext/json/VERSION1
-rw-r--r--ext/json/depend2
-rw-r--r--ext/json/extconf.rb3
-rw-r--r--ext/json/fbuffer/fbuffer.h187
-rw-r--r--ext/json/generator/depend181
-rw-r--r--ext/json/generator/extconf.rb4
-rw-r--r--ext/json/generator/generator.c1608
-rw-r--r--ext/json/generator/generator.h174
-rw-r--r--ext/json/json.gemspec67
-rw-r--r--ext/json/lib/json.rb583
-rw-r--r--ext/json/lib/json/add/bigdecimal.rb29
-rw-r--r--ext/json/lib/json/add/complex.rb28
-rw-r--r--ext/json/lib/json/add/core.rb12
-rw-r--r--ext/json/lib/json/add/date.rb34
-rw-r--r--ext/json/lib/json/add/date_time.rb50
-rw-r--r--ext/json/lib/json/add/exception.rb31
-rw-r--r--ext/json/lib/json/add/ostruct.rb31
-rw-r--r--ext/json/lib/json/add/range.rb29
-rw-r--r--ext/json/lib/json/add/rational.rb27
-rw-r--r--ext/json/lib/json/add/regexp.rb30
-rw-r--r--ext/json/lib/json/add/set.rb29
-rw-r--r--ext/json/lib/json/add/struct.rb30
-rw-r--r--ext/json/lib/json/add/symbol.rb25
-rw-r--r--ext/json/lib/json/add/time.rb38
-rw-r--r--ext/json/lib/json/common.rb703
-rw-r--r--ext/json/lib/json/ext.rb15
-rw-r--r--ext/json/lib/json/generic_object.rb71
-rw-r--r--ext/json/lib/json/version.rb9
-rw-r--r--ext/json/parser/depend179
-rw-r--r--ext/json/parser/extconf.rb32
-rw-r--r--ext/json/parser/parser.c3338
-rw-r--r--ext/json/parser/parser.h96
-rw-r--r--ext/json/parser/parser.rl977
-rw-r--r--ext/json/parser/prereq.mk13
34 files changed, 8666 insertions, 0 deletions
diff --git a/ext/json/VERSION b/ext/json/VERSION
new file mode 100644
index 0000000000..6a6a3d8e35
--- /dev/null
+++ b/ext/json/VERSION
@@ -0,0 +1 @@
+2.6.1
diff --git a/ext/json/depend b/ext/json/depend
new file mode 100644
index 0000000000..0301ce074c
--- /dev/null
+++ b/ext/json/depend
@@ -0,0 +1,2 @@
+# AUTOGENERATED DEPENDENCIES START
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/json/extconf.rb b/ext/json/extconf.rb
new file mode 100644
index 0000000000..8a99b6a5c8
--- /dev/null
+++ b/ext/json/extconf.rb
@@ -0,0 +1,3 @@
+require 'mkmf'
+
+create_makefile('json')
diff --git a/ext/json/fbuffer/fbuffer.h b/ext/json/fbuffer/fbuffer.h
new file mode 100644
index 0000000000..dc8f406b5b
--- /dev/null
+++ b/ext/json/fbuffer/fbuffer.h
@@ -0,0 +1,187 @@
+
+#ifndef _FBUFFER_H_
+#define _FBUFFER_H_
+
+#include "ruby.h"
+
+#ifndef RHASH_SIZE
+#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
+#endif
+
+#ifndef RFLOAT_VALUE
+#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
+#endif
+
+#ifndef RARRAY_LEN
+#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
+#endif
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(string) RSTRING(string)->ptr
+#endif
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(string) RSTRING(string)->len
+#endif
+
+#ifdef PRIsVALUE
+# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
+# define RB_OBJ_STRING(obj) (obj)
+#else
+# define PRIsVALUE "s"
+# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
+# define RB_OBJ_STRING(obj) StringValueCStr(obj)
+#endif
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
+#else
+#define FORCE_UTF8(obj)
+#endif
+
+/* We don't need to guard objects for rbx, so let's do nothing at all. */
+#ifndef RB_GC_GUARD
+#define RB_GC_GUARD(object)
+#endif
+
+typedef struct FBufferStruct {
+ unsigned long initial_length;
+ char *ptr;
+ unsigned long len;
+ unsigned long capa;
+} FBuffer;
+
+#define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
+
+#define FBUFFER_PTR(fb) (fb->ptr)
+#define FBUFFER_LEN(fb) (fb->len)
+#define FBUFFER_CAPA(fb) (fb->capa)
+#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
+
+static FBuffer *fbuffer_alloc(unsigned long initial_length);
+static void fbuffer_free(FBuffer *fb);
+static void fbuffer_clear(FBuffer *fb);
+static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
+#ifdef JSON_GENERATOR
+static void fbuffer_append_long(FBuffer *fb, long number);
+#endif
+static void fbuffer_append_char(FBuffer *fb, char newchr);
+#ifdef JSON_GENERATOR
+static FBuffer *fbuffer_dup(FBuffer *fb);
+static VALUE fbuffer_to_s(FBuffer *fb);
+#endif
+
+static FBuffer *fbuffer_alloc(unsigned long initial_length)
+{
+ FBuffer *fb;
+ if (initial_length <= 0) initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
+ fb = ALLOC(FBuffer);
+ memset((void *) fb, 0, sizeof(FBuffer));
+ fb->initial_length = initial_length;
+ return fb;
+}
+
+static void fbuffer_free(FBuffer *fb)
+{
+ if (fb->ptr) ruby_xfree(fb->ptr);
+ ruby_xfree(fb);
+}
+
+static void fbuffer_clear(FBuffer *fb)
+{
+ fb->len = 0;
+}
+
+static void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
+{
+ unsigned long required;
+
+ if (!fb->ptr) {
+ fb->ptr = ALLOC_N(char, fb->initial_length);
+ fb->capa = fb->initial_length;
+ }
+
+ for (required = fb->capa; requested > required - fb->len; required <<= 1);
+
+ if (required > fb->capa) {
+ REALLOC_N(fb->ptr, char, required);
+ fb->capa = required;
+ }
+}
+
+static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
+{
+ if (len > 0) {
+ fbuffer_inc_capa(fb, len);
+ MEMCPY(fb->ptr + fb->len, newstr, char, len);
+ fb->len += len;
+ }
+}
+
+#ifdef JSON_GENERATOR
+static void fbuffer_append_str(FBuffer *fb, VALUE str)
+{
+ const char *newstr = StringValuePtr(str);
+ unsigned long len = RSTRING_LEN(str);
+
+ RB_GC_GUARD(str);
+
+ fbuffer_append(fb, newstr, len);
+}
+#endif
+
+static void fbuffer_append_char(FBuffer *fb, char newchr)
+{
+ fbuffer_inc_capa(fb, 1);
+ *(fb->ptr + fb->len) = newchr;
+ fb->len++;
+}
+
+#ifdef JSON_GENERATOR
+static void freverse(char *start, char *end)
+{
+ char c;
+
+ while (end > start) {
+ c = *end, *end-- = *start, *start++ = c;
+ }
+}
+
+static long fltoa(long number, char *buf)
+{
+ static char digits[] = "0123456789";
+ long sign = number;
+ char* tmp = buf;
+
+ if (sign < 0) number = -number;
+ do *tmp++ = digits[number % 10]; while (number /= 10);
+ if (sign < 0) *tmp++ = '-';
+ freverse(buf, tmp - 1);
+ return tmp - buf;
+}
+
+static void fbuffer_append_long(FBuffer *fb, long number)
+{
+ char buf[20];
+ unsigned long len = fltoa(number, buf);
+ fbuffer_append(fb, buf, len);
+}
+
+static FBuffer *fbuffer_dup(FBuffer *fb)
+{
+ unsigned long len = fb->len;
+ FBuffer *result;
+
+ result = fbuffer_alloc(len);
+ fbuffer_append(result, FBUFFER_PAIR(fb));
+ return result;
+}
+
+static VALUE fbuffer_to_s(FBuffer *fb)
+{
+ VALUE result = rb_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
+ fbuffer_free(fb);
+ FORCE_UTF8(result);
+ return result;
+}
+#endif
+#endif
diff --git a/ext/json/generator/depend b/ext/json/generator/depend
new file mode 100644
index 0000000000..1de5fb0b93
--- /dev/null
+++ b/ext/json/generator/depend
@@ -0,0 +1,181 @@
+$(OBJS): $(ruby_headers)
+generator.o: generator.c generator.h $(srcdir)/../fbuffer/fbuffer.h
+
+# AUTOGENERATED DEPENDENCIES START
+generator.o: $(RUBY_EXTCONF_H)
+generator.o: $(arch_hdrdir)/ruby/config.h
+generator.o: $(hdrdir)/ruby.h
+generator.o: $(hdrdir)/ruby/assert.h
+generator.o: $(hdrdir)/ruby/backward.h
+generator.o: $(hdrdir)/ruby/backward/2/assume.h
+generator.o: $(hdrdir)/ruby/backward/2/attributes.h
+generator.o: $(hdrdir)/ruby/backward/2/bool.h
+generator.o: $(hdrdir)/ruby/backward/2/inttypes.h
+generator.o: $(hdrdir)/ruby/backward/2/limits.h
+generator.o: $(hdrdir)/ruby/backward/2/long_long.h
+generator.o: $(hdrdir)/ruby/backward/2/stdalign.h
+generator.o: $(hdrdir)/ruby/backward/2/stdarg.h
+generator.o: $(hdrdir)/ruby/defines.h
+generator.o: $(hdrdir)/ruby/encoding.h
+generator.o: $(hdrdir)/ruby/intern.h
+generator.o: $(hdrdir)/ruby/internal/anyargs.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+generator.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+generator.o: $(hdrdir)/ruby/internal/assume.h
+generator.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+generator.o: $(hdrdir)/ruby/internal/attr/artificial.h
+generator.o: $(hdrdir)/ruby/internal/attr/cold.h
+generator.o: $(hdrdir)/ruby/internal/attr/const.h
+generator.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+generator.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+generator.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+generator.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+generator.o: $(hdrdir)/ruby/internal/attr/error.h
+generator.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+generator.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+generator.o: $(hdrdir)/ruby/internal/attr/format.h
+generator.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+generator.o: $(hdrdir)/ruby/internal/attr/noalias.h
+generator.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+generator.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+generator.o: $(hdrdir)/ruby/internal/attr/noinline.h
+generator.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+generator.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+generator.o: $(hdrdir)/ruby/internal/attr/pure.h
+generator.o: $(hdrdir)/ruby/internal/attr/restrict.h
+generator.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+generator.o: $(hdrdir)/ruby/internal/attr/warning.h
+generator.o: $(hdrdir)/ruby/internal/attr/weakref.h
+generator.o: $(hdrdir)/ruby/internal/cast.h
+generator.o: $(hdrdir)/ruby/internal/compiler_is.h
+generator.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+generator.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+generator.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+generator.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+generator.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+generator.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+generator.o: $(hdrdir)/ruby/internal/compiler_since.h
+generator.o: $(hdrdir)/ruby/internal/config.h
+generator.o: $(hdrdir)/ruby/internal/constant_p.h
+generator.o: $(hdrdir)/ruby/internal/core.h
+generator.o: $(hdrdir)/ruby/internal/core/rarray.h
+generator.o: $(hdrdir)/ruby/internal/core/rbasic.h
+generator.o: $(hdrdir)/ruby/internal/core/rbignum.h
+generator.o: $(hdrdir)/ruby/internal/core/rclass.h
+generator.o: $(hdrdir)/ruby/internal/core/rdata.h
+generator.o: $(hdrdir)/ruby/internal/core/rfile.h
+generator.o: $(hdrdir)/ruby/internal/core/rhash.h
+generator.o: $(hdrdir)/ruby/internal/core/rmatch.h
+generator.o: $(hdrdir)/ruby/internal/core/robject.h
+generator.o: $(hdrdir)/ruby/internal/core/rregexp.h
+generator.o: $(hdrdir)/ruby/internal/core/rstring.h
+generator.o: $(hdrdir)/ruby/internal/core/rstruct.h
+generator.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+generator.o: $(hdrdir)/ruby/internal/ctype.h
+generator.o: $(hdrdir)/ruby/internal/dllexport.h
+generator.o: $(hdrdir)/ruby/internal/dosish.h
+generator.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+generator.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+generator.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+generator.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+generator.o: $(hdrdir)/ruby/internal/encoding/re.h
+generator.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+generator.o: $(hdrdir)/ruby/internal/encoding/string.h
+generator.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+generator.o: $(hdrdir)/ruby/internal/encoding/transcode.h
+generator.o: $(hdrdir)/ruby/internal/error.h
+generator.o: $(hdrdir)/ruby/internal/eval.h
+generator.o: $(hdrdir)/ruby/internal/event.h
+generator.o: $(hdrdir)/ruby/internal/fl_type.h
+generator.o: $(hdrdir)/ruby/internal/gc.h
+generator.o: $(hdrdir)/ruby/internal/glob.h
+generator.o: $(hdrdir)/ruby/internal/globals.h
+generator.o: $(hdrdir)/ruby/internal/has/attribute.h
+generator.o: $(hdrdir)/ruby/internal/has/builtin.h
+generator.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+generator.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+generator.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+generator.o: $(hdrdir)/ruby/internal/has/extension.h
+generator.o: $(hdrdir)/ruby/internal/has/feature.h
+generator.o: $(hdrdir)/ruby/internal/has/warning.h
+generator.o: $(hdrdir)/ruby/internal/intern/array.h
+generator.o: $(hdrdir)/ruby/internal/intern/bignum.h
+generator.o: $(hdrdir)/ruby/internal/intern/class.h
+generator.o: $(hdrdir)/ruby/internal/intern/compar.h
+generator.o: $(hdrdir)/ruby/internal/intern/complex.h
+generator.o: $(hdrdir)/ruby/internal/intern/cont.h
+generator.o: $(hdrdir)/ruby/internal/intern/dir.h
+generator.o: $(hdrdir)/ruby/internal/intern/enum.h
+generator.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+generator.o: $(hdrdir)/ruby/internal/intern/error.h
+generator.o: $(hdrdir)/ruby/internal/intern/eval.h
+generator.o: $(hdrdir)/ruby/internal/intern/file.h
+generator.o: $(hdrdir)/ruby/internal/intern/gc.h
+generator.o: $(hdrdir)/ruby/internal/intern/hash.h
+generator.o: $(hdrdir)/ruby/internal/intern/io.h
+generator.o: $(hdrdir)/ruby/internal/intern/load.h
+generator.o: $(hdrdir)/ruby/internal/intern/marshal.h
+generator.o: $(hdrdir)/ruby/internal/intern/numeric.h
+generator.o: $(hdrdir)/ruby/internal/intern/object.h
+generator.o: $(hdrdir)/ruby/internal/intern/parse.h
+generator.o: $(hdrdir)/ruby/internal/intern/proc.h
+generator.o: $(hdrdir)/ruby/internal/intern/process.h
+generator.o: $(hdrdir)/ruby/internal/intern/random.h
+generator.o: $(hdrdir)/ruby/internal/intern/range.h
+generator.o: $(hdrdir)/ruby/internal/intern/rational.h
+generator.o: $(hdrdir)/ruby/internal/intern/re.h
+generator.o: $(hdrdir)/ruby/internal/intern/ruby.h
+generator.o: $(hdrdir)/ruby/internal/intern/select.h
+generator.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+generator.o: $(hdrdir)/ruby/internal/intern/signal.h
+generator.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+generator.o: $(hdrdir)/ruby/internal/intern/string.h
+generator.o: $(hdrdir)/ruby/internal/intern/struct.h
+generator.o: $(hdrdir)/ruby/internal/intern/thread.h
+generator.o: $(hdrdir)/ruby/internal/intern/time.h
+generator.o: $(hdrdir)/ruby/internal/intern/variable.h
+generator.o: $(hdrdir)/ruby/internal/intern/vm.h
+generator.o: $(hdrdir)/ruby/internal/interpreter.h
+generator.o: $(hdrdir)/ruby/internal/iterator.h
+generator.o: $(hdrdir)/ruby/internal/memory.h
+generator.o: $(hdrdir)/ruby/internal/method.h
+generator.o: $(hdrdir)/ruby/internal/module.h
+generator.o: $(hdrdir)/ruby/internal/newobj.h
+generator.o: $(hdrdir)/ruby/internal/rgengc.h
+generator.o: $(hdrdir)/ruby/internal/scan_args.h
+generator.o: $(hdrdir)/ruby/internal/special_consts.h
+generator.o: $(hdrdir)/ruby/internal/static_assert.h
+generator.o: $(hdrdir)/ruby/internal/stdalign.h
+generator.o: $(hdrdir)/ruby/internal/stdbool.h
+generator.o: $(hdrdir)/ruby/internal/symbol.h
+generator.o: $(hdrdir)/ruby/internal/value.h
+generator.o: $(hdrdir)/ruby/internal/value_type.h
+generator.o: $(hdrdir)/ruby/internal/variable.h
+generator.o: $(hdrdir)/ruby/internal/warning_push.h
+generator.o: $(hdrdir)/ruby/internal/xmalloc.h
+generator.o: $(hdrdir)/ruby/missing.h
+generator.o: $(hdrdir)/ruby/onigmo.h
+generator.o: $(hdrdir)/ruby/oniguruma.h
+generator.o: $(hdrdir)/ruby/re.h
+generator.o: $(hdrdir)/ruby/regex.h
+generator.o: $(hdrdir)/ruby/ruby.h
+generator.o: $(hdrdir)/ruby/st.h
+generator.o: $(hdrdir)/ruby/subst.h
+generator.o: $(srcdir)/../fbuffer/fbuffer.h
+generator.o: generator.c
+generator.o: generator.h
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/json/generator/extconf.rb b/ext/json/generator/extconf.rb
new file mode 100644
index 0000000000..8627c5f4bd
--- /dev/null
+++ b/ext/json/generator/extconf.rb
@@ -0,0 +1,4 @@
+require 'mkmf'
+
+$defs << "-DJSON_GENERATOR"
+create_makefile 'json/ext/generator'
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
new file mode 100644
index 0000000000..e3a83472e1
--- /dev/null
+++ b/ext/json/generator/generator.c
@@ -0,0 +1,1608 @@
+#include "../fbuffer/fbuffer.h"
+#include "generator.h"
+
+static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
+ mHash, mArray,
+#ifdef RUBY_INTEGER_UNIFICATION
+ mInteger,
+#else
+ mFixnum, mBignum,
+#endif
+ mFloat, mString, mString_Extend,
+ mTrueClass, mFalseClass, mNilClass, eGeneratorError,
+ eNestingError;
+
+static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
+ i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
+ i_pack, i_unpack, i_create_id, i_extend, i_key_p,
+ i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
+ i_buffer_initial_length, i_dup, i_escape_slash;
+
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns 0. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length)
+{
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return 0;
+ /* Everything else falls through when "1"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return 0; break;
+ case 0xED: if (a > 0x9F) return 0; break;
+ case 0xF0: if (a < 0x90) return 0; break;
+ case 0xF4: if (a > 0x8F) return 0; break;
+ default: if (a < 0x80) return 0;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
+ }
+ if (*source > 0xF4) return 0;
+ return 1;
+}
+
+/* Escapes the UTF16 character and stores the result in the buffer buf. */
+static void unicode_escape(char *buf, UTF16 character)
+{
+ const char *digits = "0123456789abcdef";
+
+ buf[2] = digits[character >> 12];
+ buf[3] = digits[(character >> 8) & 0xf];
+ buf[4] = digits[(character >> 4) & 0xf];
+ buf[5] = digits[character & 0xf];
+}
+
+/* Escapes the UTF16 character and stores the result in the buffer buf, then
+ * the buffer buf is appended to the FBuffer buffer. */
+static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
+ character)
+{
+ unicode_escape(buf, character);
+ fbuffer_append(buffer, buf, 6);
+}
+
+/* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
+ * and control characters are JSON escaped. */
+static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash)
+{
+ const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
+ const UTF8 *sourceEnd = source + RSTRING_LEN(string);
+ char buf[6] = { '\\', 'u' };
+
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "partial character in source, but hit end");
+ }
+ if (!isLegalUTF8(source, extraBytesToRead+1)) {
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "source sequence is illegal/malformed utf-8");
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+#if UNI_STRICT_CONVERSION
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "source sequence is illegal/malformed utf-8");
+#else
+ unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
+#endif
+ } else {
+ /* normal case */
+ if (ch >= 0x20 && ch <= 0x7f) {
+ switch (ch) {
+ case '\\':
+ fbuffer_append(buffer, "\\\\", 2);
+ break;
+ case '"':
+ fbuffer_append(buffer, "\\\"", 2);
+ break;
+ case '/':
+ if(escape_slash) {
+ fbuffer_append(buffer, "\\/", 2);
+ break;
+ }
+ default:
+ fbuffer_append_char(buffer, (char)ch);
+ break;
+ }
+ } else {
+ switch (ch) {
+ case '\n':
+ fbuffer_append(buffer, "\\n", 2);
+ break;
+ case '\r':
+ fbuffer_append(buffer, "\\r", 2);
+ break;
+ case '\t':
+ fbuffer_append(buffer, "\\t", 2);
+ break;
+ case '\f':
+ fbuffer_append(buffer, "\\f", 2);
+ break;
+ case '\b':
+ fbuffer_append(buffer, "\\b", 2);
+ break;
+ default:
+ unicode_escape_to_buffer(buffer, buf, (UTF16) ch);
+ break;
+ }
+ }
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+#if UNI_STRICT_CONVERSION
+ source -= (extraBytesToRead+1); /* return to the start */
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "source sequence is illegal/malformed utf8");
+#else
+ unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
+#endif
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ ch -= halfBase;
+ unicode_escape_to_buffer(buffer, buf, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START));
+ unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
+ }
+ }
+ RB_GC_GUARD(string);
+}
+
+/* Converts string to a JSON string in FBuffer buffer, where only the
+ * characters required by the JSON standard are JSON escaped. The remaining
+ * characters (should be UTF8) are just passed through and appended to the
+ * result. */
+static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash)
+{
+ const char *ptr = RSTRING_PTR(string), *p;
+ unsigned long len = RSTRING_LEN(string), start = 0, end = 0;
+ const char *escape = NULL;
+ int escape_len;
+ unsigned char c;
+ char buf[6] = { '\\', 'u' };
+ int ascii_only = rb_enc_str_asciionly_p(string);
+
+ for (start = 0, end = 0; end < len;) {
+ p = ptr + end;
+ c = (unsigned char) *p;
+ if (c < 0x20) {
+ switch (c) {
+ case '\n':
+ escape = "\\n";
+ escape_len = 2;
+ break;
+ case '\r':
+ escape = "\\r";
+ escape_len = 2;
+ break;
+ case '\t':
+ escape = "\\t";
+ escape_len = 2;
+ break;
+ case '\f':
+ escape = "\\f";
+ escape_len = 2;
+ break;
+ case '\b':
+ escape = "\\b";
+ escape_len = 2;
+ break;
+ default:
+ unicode_escape(buf, (UTF16) *p);
+ escape = buf;
+ escape_len = 6;
+ break;
+ }
+ } else {
+ switch (c) {
+ case '\\':
+ escape = "\\\\";
+ escape_len = 2;
+ break;
+ case '"':
+ escape = "\\\"";
+ escape_len = 2;
+ break;
+ case '/':
+ if(escape_slash) {
+ escape = "\\/";
+ escape_len = 2;
+ break;
+ }
+ default:
+ {
+ unsigned short clen = 1;
+ if (!ascii_only) {
+ clen += trailingBytesForUTF8[c];
+ if (end + clen > len) {
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "partial character in source, but hit end");
+ }
+ if (!isLegalUTF8((UTF8 *) p, clen)) {
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "source sequence is illegal/malformed utf-8");
+ }
+ }
+ end += clen;
+ }
+ continue;
+ break;
+ }
+ }
+ fbuffer_append(buffer, ptr + start, end - start);
+ fbuffer_append(buffer, escape, escape_len);
+ start = ++end;
+ escape = NULL;
+ }
+ fbuffer_append(buffer, ptr + start, end - start);
+}
+
+static char *fstrndup(const char *ptr, unsigned long len) {
+ char *result;
+ if (len <= 0) return NULL;
+ result = ALLOC_N(char, len);
+ memcpy(result, ptr, len);
+ return result;
+}
+
+/*
+ * Document-module: JSON::Ext::Generator
+ *
+ * This is the JSON generator implemented as a C extension. It can be
+ * configured to be used by setting
+ *
+ * JSON.generator = JSON::Ext::Generator
+ *
+ * with the method generator= in JSON.
+ *
+ */
+
+/* Explanation of the following: that's the only way to not pollute
+ * standard library's docs with GeneratorMethods::<ClassName> which
+ * are uninformative and take a large place in a list of classes
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Array
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Float
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Object
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::String
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
+ * :nodoc:
+ */
+
+/*
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
+ * :nodoc:
+ */
+
+/*
+ * call-seq: to_json(state = nil)
+ *
+ * Returns a JSON string containing a JSON object, that is generated from
+ * this Hash instance.
+ * _state_ is a JSON::State object, that can also be used to configure the
+ * produced JSON string output further.
+ */
+static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(object);
+}
+
+/*
+ * call-seq: to_json(state = nil)
+ *
+ * Returns a JSON string containing a JSON array, that is generated from
+ * this Array instance.
+ * _state_ is a JSON::State object, that can also be used to configure the
+ * produced JSON string output further.
+ */
+static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
+ GENERATE_JSON(array);
+}
+
+#ifdef RUBY_INTEGER_UNIFICATION
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string representation for this Integer number.
+ */
+static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(integer);
+}
+
+#else
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string representation for this Integer number.
+ */
+static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(fixnum);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string representation for this Integer number.
+ */
+static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(bignum);
+}
+#endif
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string representation for this Float number.
+ */
+static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(float);
+}
+
+/*
+ * call-seq: String.included(modul)
+ *
+ * Extends _modul_ with the String::Extend module.
+ */
+static VALUE mString_included_s(VALUE self, VALUE modul) {
+ VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
+ return result;
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * This string should be encoded with UTF-8 A call to this method
+ * returns a JSON string encoded with UTF16 big endian characters as
+ * \u????.
+ */
+static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(string);
+}
+
+/*
+ * call-seq: to_json_raw_object()
+ *
+ * This method creates a raw object hash, that can be nested into
+ * other data structures and will be generated as a raw string. This
+ * method should be used, if you want to convert raw strings to JSON
+ * instead of UTF-8 strings, e. g. binary data.
+ */
+static VALUE mString_to_json_raw_object(VALUE self)
+{
+ VALUE ary;
+ VALUE result = rb_hash_new();
+ rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
+ ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
+ rb_hash_aset(result, rb_str_new2("raw"), ary);
+ return result;
+}
+
+/*
+ * call-seq: to_json_raw(*args)
+ *
+ * This method creates a JSON text from the result of a call to
+ * to_json_raw_object of this String.
+ */
+static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
+{
+ VALUE obj = mString_to_json_raw_object(self);
+ Check_Type(obj, T_HASH);
+ return mHash_to_json(argc, argv, obj);
+}
+
+/*
+ * call-seq: json_create(o)
+ *
+ * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
+ * key "raw"). The Ruby String can be created by this module method.
+ */
+static VALUE mString_Extend_json_create(VALUE self, VALUE o)
+{
+ VALUE ary;
+ Check_Type(o, T_HASH);
+ ary = rb_hash_aref(o, rb_str_new2("raw"));
+ return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string for true: 'true'.
+ */
+static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(true);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string for false: 'false'.
+ */
+static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(false);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string for nil: 'null'.
+ */
+static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
+{
+ GENERATE_JSON(null);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Converts this object to a string (calling #to_s), converts
+ * it to a JSON string, and returns the result. This is a fallback, if no
+ * special method #to_json was defined for some object.
+ */
+static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state;
+ VALUE string = rb_funcall(self, i_to_s, 0);
+ rb_scan_args(argc, argv, "01", &state);
+ Check_Type(string, T_STRING);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, string);
+}
+
+static void State_free(void *ptr)
+{
+ JSON_Generator_State *state = ptr;
+ if (state->indent) ruby_xfree(state->indent);
+ if (state->space) ruby_xfree(state->space);
+ if (state->space_before) ruby_xfree(state->space_before);
+ if (state->object_nl) ruby_xfree(state->object_nl);
+ if (state->array_nl) ruby_xfree(state->array_nl);
+ if (state->array_delim) fbuffer_free(state->array_delim);
+ if (state->object_delim) fbuffer_free(state->object_delim);
+ if (state->object_delim2) fbuffer_free(state->object_delim2);
+ ruby_xfree(state);
+}
+
+static size_t State_memsize(const void *ptr)
+{
+ const JSON_Generator_State *state = ptr;
+ size_t size = sizeof(*state);
+ if (state->indent) size += state->indent_len + 1;
+ if (state->space) size += state->space_len + 1;
+ if (state->space_before) size += state->space_before_len + 1;
+ if (state->object_nl) size += state->object_nl_len + 1;
+ if (state->array_nl) size += state->array_nl_len + 1;
+ if (state->array_delim) size += FBUFFER_CAPA(state->array_delim);
+ if (state->object_delim) size += FBUFFER_CAPA(state->object_delim);
+ if (state->object_delim2) size += FBUFFER_CAPA(state->object_delim2);
+ return size;
+}
+
+#ifndef HAVE_RB_EXT_RACTOR_SAFE
+# undef RUBY_TYPED_FROZEN_SHAREABLE
+# define RUBY_TYPED_FROZEN_SHAREABLE 0
+#endif
+
+#ifdef NEW_TYPEDDATA_WRAPPER
+static const rb_data_type_t JSON_Generator_State_type = {
+ "JSON/Generator/State",
+ {NULL, State_free, State_memsize,},
+#ifdef RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
+#endif
+};
+#endif
+
+static VALUE cState_s_allocate(VALUE klass)
+{
+ JSON_Generator_State *state;
+ return TypedData_Make_Struct(klass, JSON_Generator_State,
+ &JSON_Generator_State_type, state);
+}
+
+/*
+ * call-seq: configure(opts)
+ *
+ * Configure this State instance with the Hash _opts_, and return
+ * itself.
+ */
+static VALUE cState_configure(VALUE self, VALUE opts)
+{
+ VALUE tmp;
+ GET_STATE(self);
+ tmp = rb_check_convert_type(opts, T_HASH, "Hash", "to_hash");
+ if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
+ opts = tmp;
+ tmp = rb_hash_aref(opts, ID2SYM(i_indent));
+ if (RTEST(tmp)) {
+ unsigned long len;
+ Check_Type(tmp, T_STRING);
+ len = RSTRING_LEN(tmp);
+ state->indent = fstrndup(RSTRING_PTR(tmp), len + 1);
+ state->indent_len = len;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_space));
+ if (RTEST(tmp)) {
+ unsigned long len;
+ Check_Type(tmp, T_STRING);
+ len = RSTRING_LEN(tmp);
+ state->space = fstrndup(RSTRING_PTR(tmp), len + 1);
+ state->space_len = len;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
+ if (RTEST(tmp)) {
+ unsigned long len;
+ Check_Type(tmp, T_STRING);
+ len = RSTRING_LEN(tmp);
+ state->space_before = fstrndup(RSTRING_PTR(tmp), len + 1);
+ state->space_before_len = len;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
+ if (RTEST(tmp)) {
+ unsigned long len;
+ Check_Type(tmp, T_STRING);
+ len = RSTRING_LEN(tmp);
+ state->array_nl = fstrndup(RSTRING_PTR(tmp), len + 1);
+ state->array_nl_len = len;
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
+ if (RTEST(tmp)) {
+ unsigned long len;
+ Check_Type(tmp, T_STRING);
+ len = RSTRING_LEN(tmp);
+ state->object_nl = fstrndup(RSTRING_PTR(tmp), len + 1);
+ state->object_nl_len = len;
+ }
+ tmp = ID2SYM(i_max_nesting);
+ state->max_nesting = 100;
+ if (option_given_p(opts, tmp)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
+ if (RTEST(max_nesting)) {
+ Check_Type(max_nesting, T_FIXNUM);
+ state->max_nesting = FIX2LONG(max_nesting);
+ } else {
+ state->max_nesting = 0;
+ }
+ }
+ tmp = ID2SYM(i_depth);
+ state->depth = 0;
+ if (option_given_p(opts, tmp)) {
+ VALUE depth = rb_hash_aref(opts, tmp);
+ if (RTEST(depth)) {
+ Check_Type(depth, T_FIXNUM);
+ state->depth = FIX2LONG(depth);
+ } else {
+ state->depth = 0;
+ }
+ }
+ tmp = ID2SYM(i_buffer_initial_length);
+ if (option_given_p(opts, tmp)) {
+ VALUE buffer_initial_length = rb_hash_aref(opts, tmp);
+ if (RTEST(buffer_initial_length)) {
+ long initial_length;
+ Check_Type(buffer_initial_length, T_FIXNUM);
+ initial_length = FIX2LONG(buffer_initial_length);
+ if (initial_length > 0) state->buffer_initial_length = initial_length;
+ }
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
+ state->allow_nan = RTEST(tmp);
+ tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
+ state->ascii_only = RTEST(tmp);
+ tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
+ state->escape_slash = RTEST(tmp);
+ return self;
+}
+
+static void set_state_ivars(VALUE hash, VALUE state)
+{
+ VALUE ivars = rb_obj_instance_variables(state);
+ int i = 0;
+ for (i = 0; i < RARRAY_LEN(ivars); i++) {
+ VALUE key = rb_funcall(rb_ary_entry(ivars, i), i_to_s, 0);
+ long key_len = RSTRING_LEN(key);
+ VALUE value = rb_iv_get(state, StringValueCStr(key));
+ rb_hash_aset(hash, rb_str_intern(rb_str_substr(key, 1, key_len - 1)), value);
+ }
+}
+
+/*
+ * call-seq: to_h
+ *
+ * Returns the configuration instance variables as a hash, that can be
+ * passed to the configure method.
+ */
+static VALUE cState_to_h(VALUE self)
+{
+ VALUE result = rb_hash_new();
+ GET_STATE(self);
+ set_state_ivars(result, self);
+ rb_hash_aset(result, ID2SYM(i_indent), rb_str_new(state->indent, state->indent_len));
+ rb_hash_aset(result, ID2SYM(i_space), rb_str_new(state->space, state->space_len));
+ rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new(state->space_before, state->space_before_len));
+ rb_hash_aset(result, ID2SYM(i_object_nl), rb_str_new(state->object_nl, state->object_nl_len));
+ rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len));
+ rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
+ rb_hash_aset(result, ID2SYM(i_escape_slash), state->escape_slash ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
+ rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length));
+ return result;
+}
+
+/*
+* call-seq: [](name)
+*
+* Returns the value returned by method +name+.
+*/
+static VALUE cState_aref(VALUE self, VALUE name)
+{
+ name = rb_funcall(name, i_to_s, 0);
+ if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) {
+ return rb_funcall(self, i_send, 1, name);
+ } else {
+ return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)));
+ }
+}
+
+/*
+* call-seq: []=(name, value)
+*
+* Sets the attribute name to value.
+*/
+static VALUE cState_aset(VALUE self, VALUE name, VALUE value)
+{
+ VALUE name_writer;
+
+ name = rb_funcall(name, i_to_s, 0);
+ name_writer = rb_str_cat2(rb_str_dup(name), "=");
+ if (RTEST(rb_funcall(self, i_respond_to_p, 1, name_writer))) {
+ return rb_funcall(self, i_send, 2, name_writer, value);
+ } else {
+ rb_ivar_set(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)), value);
+ }
+ return Qnil;
+}
+
+struct hash_foreach_arg {
+ FBuffer *buffer;
+ JSON_Generator_State *state;
+ VALUE Vstate;
+ int iter;
+};
+
+static int
+json_object_i(VALUE key, VALUE val, VALUE _arg)
+{
+ struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg;
+ FBuffer *buffer = arg->buffer;
+ JSON_Generator_State *state = arg->state;
+ VALUE Vstate = arg->Vstate;
+
+ char *object_nl = state->object_nl;
+ long object_nl_len = state->object_nl_len;
+ char *indent = state->indent;
+ long indent_len = state->indent_len;
+ char *delim = FBUFFER_PTR(state->object_delim);
+ long delim_len = FBUFFER_LEN(state->object_delim);
+ char *delim2 = FBUFFER_PTR(state->object_delim2);
+ long delim2_len = FBUFFER_LEN(state->object_delim2);
+ long depth = state->depth;
+ int j;
+ VALUE klass, key_to_s;
+
+ if (arg->iter > 0) fbuffer_append(buffer, delim, delim_len);
+ if (object_nl) {
+ fbuffer_append(buffer, object_nl, object_nl_len);
+ }
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+
+ klass = CLASS_OF(key);
+ if (klass == rb_cString) {
+ key_to_s = key;
+ } else if (klass == rb_cSymbol) {
+ key_to_s = rb_id2str(SYM2ID(key));
+ } else {
+ key_to_s = rb_funcall(key, i_to_s, 0);
+ }
+ Check_Type(key_to_s, T_STRING);
+ generate_json(buffer, Vstate, state, key_to_s);
+ fbuffer_append(buffer, delim2, delim2_len);
+ generate_json(buffer, Vstate, state, val);
+
+ arg->iter++;
+ return ST_CONTINUE;
+}
+
+static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ char *object_nl = state->object_nl;
+ long object_nl_len = state->object_nl_len;
+ char *indent = state->indent;
+ long indent_len = state->indent_len;
+ long max_nesting = state->max_nesting;
+ long depth = ++state->depth;
+ int j;
+ struct hash_foreach_arg arg;
+
+ if (max_nesting != 0 && depth > max_nesting) {
+ fbuffer_free(buffer);
+ rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
+ }
+ fbuffer_append_char(buffer, '{');
+
+ arg.buffer = buffer;
+ arg.state = state;
+ arg.Vstate = Vstate;
+ arg.iter = 0;
+ rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
+
+ depth = --state->depth;
+ if (object_nl) {
+ fbuffer_append(buffer, object_nl, object_nl_len);
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+ }
+ fbuffer_append_char(buffer, '}');
+}
+
+static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ char *array_nl = state->array_nl;
+ long array_nl_len = state->array_nl_len;
+ char *indent = state->indent;
+ long indent_len = state->indent_len;
+ long max_nesting = state->max_nesting;
+ char *delim = FBUFFER_PTR(state->array_delim);
+ long delim_len = FBUFFER_LEN(state->array_delim);
+ long depth = ++state->depth;
+ int i, j;
+ if (max_nesting != 0 && depth > max_nesting) {
+ fbuffer_free(buffer);
+ rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
+ }
+ fbuffer_append_char(buffer, '[');
+ if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len);
+ for(i = 0; i < RARRAY_LEN(obj); i++) {
+ if (i > 0) fbuffer_append(buffer, delim, delim_len);
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+ generate_json(buffer, Vstate, state, rb_ary_entry(obj, i));
+ }
+ state->depth = --depth;
+ if (array_nl) {
+ fbuffer_append(buffer, array_nl, array_nl_len);
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+ }
+ fbuffer_append_char(buffer, ']');
+}
+
+#ifdef HAVE_RUBY_ENCODING_H
+static int enc_utf8_compatible_p(rb_encoding *enc)
+{
+ if (enc == rb_usascii_encoding()) return 1;
+ if (enc == rb_utf8_encoding()) return 1;
+ return 0;
+}
+#endif
+
+static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ fbuffer_append_char(buffer, '"');
+#ifdef HAVE_RUBY_ENCODING_H
+ if (!enc_utf8_compatible_p(rb_enc_get(obj))) {
+ obj = rb_str_export_to_enc(obj, rb_utf8_encoding());
+ }
+#endif
+ if (state->ascii_only) {
+ convert_UTF8_to_JSON_ASCII(buffer, obj, state->escape_slash);
+ } else {
+ convert_UTF8_to_JSON(buffer, obj, state->escape_slash);
+ }
+ fbuffer_append_char(buffer, '"');
+}
+
+static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ fbuffer_append(buffer, "null", 4);
+}
+
+static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ fbuffer_append(buffer, "false", 5);
+}
+
+static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ fbuffer_append(buffer, "true", 4);
+}
+
+static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ fbuffer_append_long(buffer, FIX2LONG(obj));
+}
+
+static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ VALUE tmp = rb_funcall(obj, i_to_s, 0);
+ fbuffer_append_str(buffer, tmp);
+}
+
+#ifdef RUBY_INTEGER_UNIFICATION
+static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ if (FIXNUM_P(obj))
+ generate_json_fixnum(buffer, Vstate, state, obj);
+ else
+ generate_json_bignum(buffer, Vstate, state, obj);
+}
+#endif
+static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ double value = RFLOAT_VALUE(obj);
+ char allow_nan = state->allow_nan;
+ VALUE tmp = rb_funcall(obj, i_to_s, 0);
+ if (!allow_nan) {
+ if (isinf(value)) {
+ fbuffer_free(buffer);
+ rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
+ } else if (isnan(value)) {
+ fbuffer_free(buffer);
+ rb_raise(eGeneratorError, "%u: %"PRIsVALUE" not allowed in JSON", __LINE__, RB_OBJ_STRING(tmp));
+ }
+ }
+ fbuffer_append_str(buffer, tmp);
+}
+
+static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
+{
+ VALUE tmp;
+ VALUE klass = CLASS_OF(obj);
+ if (klass == rb_cHash) {
+ generate_json_object(buffer, Vstate, state, obj);
+ } else if (klass == rb_cArray) {
+ generate_json_array(buffer, Vstate, state, obj);
+ } else if (klass == rb_cString) {
+ generate_json_string(buffer, Vstate, state, obj);
+ } else if (obj == Qnil) {
+ generate_json_null(buffer, Vstate, state, obj);
+ } else if (obj == Qfalse) {
+ generate_json_false(buffer, Vstate, state, obj);
+ } else if (obj == Qtrue) {
+ generate_json_true(buffer, Vstate, state, obj);
+ } else if (FIXNUM_P(obj)) {
+ generate_json_fixnum(buffer, Vstate, state, obj);
+ } else if (RB_TYPE_P(obj, T_BIGNUM)) {
+ generate_json_bignum(buffer, Vstate, state, obj);
+ } else if (klass == rb_cFloat) {
+ generate_json_float(buffer, Vstate, state, obj);
+ } else if (rb_respond_to(obj, i_to_json)) {
+ tmp = rb_funcall(obj, i_to_json, 1, Vstate);
+ Check_Type(tmp, T_STRING);
+ fbuffer_append_str(buffer, tmp);
+ } else {
+ tmp = rb_funcall(obj, i_to_s, 0);
+ Check_Type(tmp, T_STRING);
+ generate_json_string(buffer, Vstate, state, tmp);
+ }
+}
+
+static FBuffer *cState_prepare_buffer(VALUE self)
+{
+ FBuffer *buffer;
+ GET_STATE(self);
+ buffer = fbuffer_alloc(state->buffer_initial_length);
+
+ if (state->object_delim) {
+ fbuffer_clear(state->object_delim);
+ } else {
+ state->object_delim = fbuffer_alloc(16);
+ }
+ fbuffer_append_char(state->object_delim, ',');
+ if (state->object_delim2) {
+ fbuffer_clear(state->object_delim2);
+ } else {
+ state->object_delim2 = fbuffer_alloc(16);
+ }
+ if (state->space_before) fbuffer_append(state->object_delim2, state->space_before, state->space_before_len);
+ fbuffer_append_char(state->object_delim2, ':');
+ if (state->space) fbuffer_append(state->object_delim2, state->space, state->space_len);
+
+ if (state->array_delim) {
+ fbuffer_clear(state->array_delim);
+ } else {
+ state->array_delim = fbuffer_alloc(16);
+ }
+ fbuffer_append_char(state->array_delim, ',');
+ if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len);
+ return buffer;
+}
+
+static VALUE cState_partial_generate(VALUE self, VALUE obj)
+{
+ FBuffer *buffer = cState_prepare_buffer(self);
+ GET_STATE(self);
+ generate_json(buffer, self, state, obj);
+ return fbuffer_to_s(buffer);
+}
+
+/*
+ * call-seq: generate(obj)
+ *
+ * Generates a valid JSON document from object +obj+ and returns the
+ * result. If no valid JSON document can be created this method raises a
+ * GeneratorError exception.
+ */
+static VALUE cState_generate(VALUE self, VALUE obj)
+{
+ VALUE result = cState_partial_generate(self, obj);
+ GET_STATE(self);
+ (void)state;
+ return result;
+}
+
+/*
+ * call-seq: new(opts = {})
+ *
+ * Instantiates a new State object, configured by _opts_.
+ *
+ * _opts_ can have the following keys:
+ *
+ * * *indent*: a string used to indent levels (default: ''),
+ * * *space*: a string that is put after, a : or , delimiter (default: ''),
+ * * *space_before*: a string that is put before a : pair delimiter (default: ''),
+ * * *object_nl*: a string that is put at the end of a JSON object (default: ''),
+ * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
+ * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
+ * generated, otherwise an exception is thrown, if these values are
+ * encountered. This options defaults to false.
+ * * *ascii_only*: true if only ASCII characters should be generated. This
+ * option defaults to false.
+ * * *buffer_initial_length*: sets the initial length of the generator's
+ * internal buffer.
+ */
+static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE opts;
+ GET_STATE(self);
+ state->max_nesting = 100;
+ state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
+ rb_scan_args(argc, argv, "01", &opts);
+ if (!NIL_P(opts)) cState_configure(self, opts);
+ return self;
+}
+
+/*
+ * call-seq: initialize_copy(orig)
+ *
+ * Initializes this object from orig if it can be duplicated/cloned and returns
+ * it.
+*/
+static VALUE cState_init_copy(VALUE obj, VALUE orig)
+{
+ JSON_Generator_State *objState, *origState;
+
+ if (obj == orig) return obj;
+ GET_STATE_TO(obj, objState);
+ GET_STATE_TO(orig, origState);
+ if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
+
+ MEMCPY(objState, origState, JSON_Generator_State, 1);
+ objState->indent = fstrndup(origState->indent, origState->indent_len);
+ objState->space = fstrndup(origState->space, origState->space_len);
+ objState->space_before = fstrndup(origState->space_before, origState->space_before_len);
+ objState->object_nl = fstrndup(origState->object_nl, origState->object_nl_len);
+ objState->array_nl = fstrndup(origState->array_nl, origState->array_nl_len);
+ if (origState->array_delim) objState->array_delim = fbuffer_dup(origState->array_delim);
+ if (origState->object_delim) objState->object_delim = fbuffer_dup(origState->object_delim);
+ if (origState->object_delim2) objState->object_delim2 = fbuffer_dup(origState->object_delim2);
+ return obj;
+}
+
+/*
+ * call-seq: from_state(opts)
+ *
+ * Creates a State object from _opts_, which ought to be Hash to create a
+ * new State instance configured by _opts_, something else to create an
+ * unconfigured instance. If _opts_ is a State object, it is just returned.
+ */
+static VALUE cState_from_state_s(VALUE self, VALUE opts)
+{
+ if (rb_obj_is_kind_of(opts, self)) {
+ return opts;
+ } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
+ return rb_funcall(self, i_new, 1, opts);
+ } else {
+ return rb_class_new_instance(0, NULL, cState);
+ }
+}
+
+/*
+ * call-seq: indent()
+ *
+ * Returns the string that is used to indent levels in the JSON text.
+ */
+static VALUE cState_indent(VALUE self)
+{
+ GET_STATE(self);
+ return state->indent ? rb_str_new(state->indent, state->indent_len) : rb_str_new2("");
+}
+
+/*
+ * call-seq: indent=(indent)
+ *
+ * Sets the string that is used to indent levels in the JSON text.
+ */
+static VALUE cState_indent_set(VALUE self, VALUE indent)
+{
+ unsigned long len;
+ GET_STATE(self);
+ Check_Type(indent, T_STRING);
+ len = RSTRING_LEN(indent);
+ if (len == 0) {
+ if (state->indent) {
+ ruby_xfree(state->indent);
+ state->indent = NULL;
+ state->indent_len = 0;
+ }
+ } else {
+ if (state->indent) ruby_xfree(state->indent);
+ state->indent = fstrndup(RSTRING_PTR(indent), len);
+ state->indent_len = len;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: space()
+ *
+ * Returns the string that is used to insert a space between the tokens in a JSON
+ * string.
+ */
+static VALUE cState_space(VALUE self)
+{
+ GET_STATE(self);
+ return state->space ? rb_str_new(state->space, state->space_len) : rb_str_new2("");
+}
+
+/*
+ * call-seq: space=(space)
+ *
+ * Sets _space_ to the string that is used to insert a space between the tokens in a JSON
+ * string.
+ */
+static VALUE cState_space_set(VALUE self, VALUE space)
+{
+ unsigned long len;
+ GET_STATE(self);
+ Check_Type(space, T_STRING);
+ len = RSTRING_LEN(space);
+ if (len == 0) {
+ if (state->space) {
+ ruby_xfree(state->space);
+ state->space = NULL;
+ state->space_len = 0;
+ }
+ } else {
+ if (state->space) ruby_xfree(state->space);
+ state->space = fstrndup(RSTRING_PTR(space), len);
+ state->space_len = len;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: space_before()
+ *
+ * Returns the string that is used to insert a space before the ':' in JSON objects.
+ */
+static VALUE cState_space_before(VALUE self)
+{
+ GET_STATE(self);
+ return state->space_before ? rb_str_new(state->space_before, state->space_before_len) : rb_str_new2("");
+}
+
+/*
+ * call-seq: space_before=(space_before)
+ *
+ * Sets the string that is used to insert a space before the ':' in JSON objects.
+ */
+static VALUE cState_space_before_set(VALUE self, VALUE space_before)
+{
+ unsigned long len;
+ GET_STATE(self);
+ Check_Type(space_before, T_STRING);
+ len = RSTRING_LEN(space_before);
+ if (len == 0) {
+ if (state->space_before) {
+ ruby_xfree(state->space_before);
+ state->space_before = NULL;
+ state->space_before_len = 0;
+ }
+ } else {
+ if (state->space_before) ruby_xfree(state->space_before);
+ state->space_before = fstrndup(RSTRING_PTR(space_before), len);
+ state->space_before_len = len;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: object_nl()
+ *
+ * This string is put at the end of a line that holds a JSON object (or
+ * Hash).
+ */
+static VALUE cState_object_nl(VALUE self)
+{
+ GET_STATE(self);
+ return state->object_nl ? rb_str_new(state->object_nl, state->object_nl_len) : rb_str_new2("");
+}
+
+/*
+ * call-seq: object_nl=(object_nl)
+ *
+ * This string is put at the end of a line that holds a JSON object (or
+ * Hash).
+ */
+static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
+{
+ unsigned long len;
+ GET_STATE(self);
+ Check_Type(object_nl, T_STRING);
+ len = RSTRING_LEN(object_nl);
+ if (len == 0) {
+ if (state->object_nl) {
+ ruby_xfree(state->object_nl);
+ state->object_nl = NULL;
+ }
+ } else {
+ if (state->object_nl) ruby_xfree(state->object_nl);
+ state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
+ state->object_nl_len = len;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: array_nl()
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_array_nl(VALUE self)
+{
+ GET_STATE(self);
+ return state->array_nl ? rb_str_new(state->array_nl, state->array_nl_len) : rb_str_new2("");
+}
+
+/*
+ * call-seq: array_nl=(array_nl)
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
+{
+ unsigned long len;
+ GET_STATE(self);
+ Check_Type(array_nl, T_STRING);
+ len = RSTRING_LEN(array_nl);
+ if (len == 0) {
+ if (state->array_nl) {
+ ruby_xfree(state->array_nl);
+ state->array_nl = NULL;
+ }
+ } else {
+ if (state->array_nl) ruby_xfree(state->array_nl);
+ state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
+ state->array_nl_len = len;
+ }
+ return Qnil;
+}
+
+
+/*
+* call-seq: check_circular?
+*
+* Returns true, if circular data structures should be checked,
+* otherwise returns false.
+*/
+static VALUE cState_check_circular_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->max_nesting ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: max_nesting
+ *
+ * This integer returns the maximum level of data structure nesting in
+ * the generated JSON, max_nesting = 0 if no maximum is checked.
+ */
+static VALUE cState_max_nesting(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->max_nesting);
+}
+
+/*
+ * call-seq: max_nesting=(depth)
+ *
+ * This sets the maximum level of data structure nesting in the generated JSON
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
+ */
+static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
+{
+ GET_STATE(self);
+ Check_Type(depth, T_FIXNUM);
+ return state->max_nesting = FIX2LONG(depth);
+}
+
+/*
+ * call-seq: escape_slash
+ *
+ * If this boolean is true, the forward slashes will be escaped in
+ * the json output.
+ */
+static VALUE cState_escape_slash(VALUE self)
+{
+ GET_STATE(self);
+ return state->escape_slash ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: escape_slash=(depth)
+ *
+ * This sets whether or not the forward slashes will be escaped in
+ * the json output.
+ */
+static VALUE cState_escape_slash_set(VALUE self, VALUE enable)
+{
+ GET_STATE(self);
+ state->escape_slash = RTEST(enable);
+ return Qnil;
+}
+
+/*
+ * call-seq: allow_nan?
+ *
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
+ * returns false.
+ */
+static VALUE cState_allow_nan_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->allow_nan ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: ascii_only?
+ *
+ * Returns true, if only ASCII characters should be generated. Otherwise
+ * returns false.
+ */
+static VALUE cState_ascii_only_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->ascii_only ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: depth
+ *
+ * This integer returns the current depth of data structure nesting.
+ */
+static VALUE cState_depth(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->depth);
+}
+
+/*
+ * call-seq: depth=(depth)
+ *
+ * This sets the maximum level of data structure nesting in the generated JSON
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
+ */
+static VALUE cState_depth_set(VALUE self, VALUE depth)
+{
+ GET_STATE(self);
+ Check_Type(depth, T_FIXNUM);
+ state->depth = FIX2LONG(depth);
+ return Qnil;
+}
+
+/*
+ * call-seq: buffer_initial_length
+ *
+ * This integer returns the current initial length of the buffer.
+ */
+static VALUE cState_buffer_initial_length(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->buffer_initial_length);
+}
+
+/*
+ * call-seq: buffer_initial_length=(length)
+ *
+ * This sets the initial length of the buffer to +length+, if +length+ > 0,
+ * otherwise its value isn't changed.
+ */
+static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
+{
+ long initial_length;
+ GET_STATE(self);
+ Check_Type(buffer_initial_length, T_FIXNUM);
+ initial_length = FIX2LONG(buffer_initial_length);
+ if (initial_length > 0) {
+ state->buffer_initial_length = initial_length;
+ }
+ return Qnil;
+}
+
+/*
+ *
+ */
+void Init_generator(void)
+{
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
+
+#undef rb_intern
+ rb_require("json/common");
+
+ mJSON = rb_define_module("JSON");
+ mExt = rb_define_module_under(mJSON, "Ext");
+ mGenerator = rb_define_module_under(mExt, "Generator");
+
+ eGeneratorError = rb_path2class("JSON::GeneratorError");
+ eNestingError = rb_path2class("JSON::NestingError");
+ rb_gc_register_mark_object(eGeneratorError);
+ rb_gc_register_mark_object(eNestingError);
+
+ cState = rb_define_class_under(mGenerator, "State", rb_cObject);
+ rb_define_alloc_func(cState, cState_s_allocate);
+ rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
+ rb_define_method(cState, "initialize", cState_initialize, -1);
+ rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
+ rb_define_method(cState, "indent", cState_indent, 0);
+ rb_define_method(cState, "indent=", cState_indent_set, 1);
+ rb_define_method(cState, "space", cState_space, 0);
+ rb_define_method(cState, "space=", cState_space_set, 1);
+ rb_define_method(cState, "space_before", cState_space_before, 0);
+ rb_define_method(cState, "space_before=", cState_space_before_set, 1);
+ rb_define_method(cState, "object_nl", cState_object_nl, 0);
+ rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
+ rb_define_method(cState, "array_nl", cState_array_nl, 0);
+ rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
+ rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
+ rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
+ rb_define_method(cState, "escape_slash", cState_escape_slash, 0);
+ rb_define_method(cState, "escape_slash?", cState_escape_slash, 0);
+ rb_define_method(cState, "escape_slash=", cState_escape_slash_set, 1);
+ rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
+ rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
+ rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
+ rb_define_method(cState, "depth", cState_depth, 0);
+ rb_define_method(cState, "depth=", cState_depth_set, 1);
+ rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
+ rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
+ rb_define_method(cState, "configure", cState_configure, 1);
+ rb_define_alias(cState, "merge", "configure");
+ rb_define_method(cState, "to_h", cState_to_h, 0);
+ rb_define_alias(cState, "to_hash", "to_h");
+ rb_define_method(cState, "[]", cState_aref, 1);
+ rb_define_method(cState, "[]=", cState_aset, 2);
+ rb_define_method(cState, "generate", cState_generate, 1);
+
+ mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
+ mObject = rb_define_module_under(mGeneratorMethods, "Object");
+ rb_define_method(mObject, "to_json", mObject_to_json, -1);
+ mHash = rb_define_module_under(mGeneratorMethods, "Hash");
+ rb_define_method(mHash, "to_json", mHash_to_json, -1);
+ mArray = rb_define_module_under(mGeneratorMethods, "Array");
+ rb_define_method(mArray, "to_json", mArray_to_json, -1);
+#ifdef RUBY_INTEGER_UNIFICATION
+ mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
+ rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
+#else
+ mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
+ rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
+ mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
+ rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
+#endif
+ mFloat = rb_define_module_under(mGeneratorMethods, "Float");
+ rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
+ mString = rb_define_module_under(mGeneratorMethods, "String");
+ rb_define_singleton_method(mString, "included", mString_included_s, 1);
+ rb_define_method(mString, "to_json", mString_to_json, -1);
+ rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
+ rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
+ mString_Extend = rb_define_module_under(mString, "Extend");
+ rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
+ mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
+ rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
+ mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
+ rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
+ mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
+ rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
+
+ i_to_s = rb_intern("to_s");
+ i_to_json = rb_intern("to_json");
+ i_new = rb_intern("new");
+ i_indent = rb_intern("indent");
+ i_space = rb_intern("space");
+ i_space_before = rb_intern("space_before");
+ i_object_nl = rb_intern("object_nl");
+ i_array_nl = rb_intern("array_nl");
+ i_max_nesting = rb_intern("max_nesting");
+ i_escape_slash = rb_intern("escape_slash");
+ i_allow_nan = rb_intern("allow_nan");
+ i_ascii_only = rb_intern("ascii_only");
+ i_depth = rb_intern("depth");
+ i_buffer_initial_length = rb_intern("buffer_initial_length");
+ i_pack = rb_intern("pack");
+ i_unpack = rb_intern("unpack");
+ i_create_id = rb_intern("create_id");
+ i_extend = rb_intern("extend");
+ i_key_p = rb_intern("key?");
+ i_aref = rb_intern("[]");
+ i_send = rb_intern("__send__");
+ i_respond_to_p = rb_intern("respond_to?");
+ i_match = rb_intern("match");
+ i_keys = rb_intern("keys");
+ i_dup = rb_intern("dup");
+}
diff --git a/ext/json/generator/generator.h b/ext/json/generator/generator.h
new file mode 100644
index 0000000000..3ebd622554
--- /dev/null
+++ b/ext/json/generator/generator.h
@@ -0,0 +1,174 @@
+#ifndef _GENERATOR_H_
+#define _GENERATOR_H_
+
+#include <math.h>
+#include <ctype.h>
+
+#include "ruby.h"
+
+#ifdef HAVE_RUBY_RE_H
+#include "ruby/re.h"
+#else
+#include "re.h"
+#endif
+
+#ifndef rb_intern_str
+#define rb_intern_str(string) SYM2ID(rb_str_intern(string))
+#endif
+
+#ifndef rb_obj_instance_variables
+#define rb_obj_instance_variables(object) rb_funcall(object, rb_intern("instance_variables"), 0)
+#endif
+
+#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
+
+/* unicode definitions */
+
+#define UNI_STRICT_CONVERSION 1
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+static unsigned char isLegalUTF8(const UTF8 *source, unsigned long length);
+static void unicode_escape(char *buf, UTF16 character);
+static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character);
+static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash);
+static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash);
+static char *fstrndup(const char *ptr, unsigned long len);
+
+/* ruby api and some helpers */
+
+typedef struct JSON_Generator_StateStruct {
+ char *indent;
+ long indent_len;
+ char *space;
+ long space_len;
+ char *space_before;
+ long space_before_len;
+ char *object_nl;
+ long object_nl_len;
+ char *array_nl;
+ long array_nl_len;
+ FBuffer *array_delim;
+ FBuffer *object_delim;
+ FBuffer *object_delim2;
+ long max_nesting;
+ char allow_nan;
+ char ascii_only;
+ char escape_slash;
+ long depth;
+ long buffer_initial_length;
+} JSON_Generator_State;
+
+#define GET_STATE_TO(self, state) \
+ TypedData_Get_Struct(self, JSON_Generator_State, &JSON_Generator_State_type, state)
+
+#define GET_STATE(self) \
+ JSON_Generator_State *state; \
+ GET_STATE_TO(self, state)
+
+#define GENERATE_JSON(type) \
+ FBuffer *buffer; \
+ VALUE Vstate; \
+ JSON_Generator_State *state; \
+ \
+ rb_scan_args(argc, argv, "01", &Vstate); \
+ Vstate = cState_from_state_s(cState, Vstate); \
+ TypedData_Get_Struct(Vstate, JSON_Generator_State, &JSON_Generator_State_type, state); \
+ buffer = cState_prepare_buffer(Vstate); \
+ generate_json_##type(buffer, Vstate, state, self); \
+ return fbuffer_to_s(buffer)
+
+static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self);
+#ifdef RUBY_INTEGER_UNIFICATION
+static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self);
+#else
+static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self);
+#endif
+static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mString_included_s(VALUE self, VALUE modul);
+static VALUE mString_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mString_to_json_raw_object(VALUE self);
+static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self);
+static VALUE mString_Extend_json_create(VALUE self, VALUE o);
+static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self);
+static void State_free(void *state);
+static VALUE cState_s_allocate(VALUE klass);
+static VALUE cState_configure(VALUE self, VALUE opts);
+static VALUE cState_to_h(VALUE self);
+static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+#ifdef RUBY_INTEGER_UNIFICATION
+static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+#endif
+static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
+static VALUE cState_partial_generate(VALUE self, VALUE obj);
+static VALUE cState_generate(VALUE self, VALUE obj);
+static VALUE cState_initialize(int argc, VALUE *argv, VALUE self);
+static VALUE cState_from_state_s(VALUE self, VALUE opts);
+static VALUE cState_indent(VALUE self);
+static VALUE cState_indent_set(VALUE self, VALUE indent);
+static VALUE cState_space(VALUE self);
+static VALUE cState_space_set(VALUE self, VALUE space);
+static VALUE cState_space_before(VALUE self);
+static VALUE cState_space_before_set(VALUE self, VALUE space_before);
+static VALUE cState_object_nl(VALUE self);
+static VALUE cState_object_nl_set(VALUE self, VALUE object_nl);
+static VALUE cState_array_nl(VALUE self);
+static VALUE cState_array_nl_set(VALUE self, VALUE array_nl);
+static VALUE cState_max_nesting(VALUE self);
+static VALUE cState_max_nesting_set(VALUE self, VALUE depth);
+static VALUE cState_allow_nan_p(VALUE self);
+static VALUE cState_ascii_only_p(VALUE self);
+static VALUE cState_depth(VALUE self);
+static VALUE cState_depth_set(VALUE self, VALUE depth);
+static VALUE cState_escape_slash(VALUE self);
+static VALUE cState_escape_slash_set(VALUE self, VALUE depth);
+static FBuffer *cState_prepare_buffer(VALUE self);
+#ifndef ZALLOC
+#define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type)))
+static inline void *ruby_zalloc(size_t n)
+{
+ void *p = ruby_xmalloc(n);
+ memset(p, 0, n);
+ return p;
+}
+#endif
+#ifdef TypedData_Make_Struct
+static const rb_data_type_t JSON_Generator_State_type;
+#define NEW_TYPEDDATA_WRAPPER 1
+#else
+#define TypedData_Make_Struct(klass, type, ignore, json) Data_Make_Struct(klass, type, NULL, State_free, json)
+#define TypedData_Get_Struct(self, JSON_Generator_State, ignore, json) Data_Get_Struct(self, JSON_Generator_State, json)
+#endif
+
+#endif
diff --git a/ext/json/json.gemspec b/ext/json/json.gemspec
new file mode 100644
index 0000000000..948e92c501
--- /dev/null
+++ b/ext/json/json.gemspec
@@ -0,0 +1,67 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = "json"
+ s.version = File.read(File.expand_path('../VERSION', __FILE__)).chomp
+
+ s.summary = "JSON Implementation for Ruby"
+ s.description = "This is a JSON implementation as a Ruby extension in C."
+ s.licenses = ["Ruby"]
+ s.authors = ["Florian Frank"]
+ s.email = "flori@ping.de"
+
+ s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"]
+ s.extra_rdoc_files = ["README.md"]
+ s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"]
+ s.files = [
+ "CHANGES.md",
+ "LICENSE",
+ "README.md",
+ "VERSION",
+ "ext/json/ext/fbuffer/fbuffer.h",
+ "ext/json/ext/generator/depend",
+ "ext/json/ext/generator/extconf.rb",
+ "ext/json/ext/generator/generator.c",
+ "ext/json/ext/generator/generator.h",
+ "ext/json/ext/parser/depend",
+ "ext/json/ext/parser/extconf.rb",
+ "ext/json/ext/parser/parser.c",
+ "ext/json/ext/parser/parser.h",
+ "ext/json/ext/parser/parser.rl",
+ "ext/json/extconf.rb",
+ "json.gemspec",
+ "lib/json.rb",
+ "lib/json/add/bigdecimal.rb",
+ "lib/json/add/complex.rb",
+ "lib/json/add/core.rb",
+ "lib/json/add/date.rb",
+ "lib/json/add/date_time.rb",
+ "lib/json/add/exception.rb",
+ "lib/json/add/ostruct.rb",
+ "lib/json/add/range.rb",
+ "lib/json/add/rational.rb",
+ "lib/json/add/regexp.rb",
+ "lib/json/add/set.rb",
+ "lib/json/add/struct.rb",
+ "lib/json/add/symbol.rb",
+ "lib/json/add/time.rb",
+ "lib/json/common.rb",
+ "lib/json/ext.rb",
+ "lib/json/generic_object.rb",
+ "lib/json/pure.rb",
+ "lib/json/pure/generator.rb",
+ "lib/json/pure/parser.rb",
+ "lib/json/version.rb",
+ ]
+ s.homepage = "http://flori.github.com/json"
+ s.metadata = {
+ 'bug_tracker_uri' => 'https://github.com/flori/json/issues',
+ 'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md',
+ 'documentation_uri' => 'http://flori.github.io/json/doc/index.html',
+ 'homepage_uri' => 'http://flori.github.io/json/',
+ 'source_code_uri' => 'https://github.com/flori/json',
+ 'wiki_uri' => 'https://github.com/flori/json/wiki'
+ }
+
+ s.required_ruby_version = Gem::Requirement.new(">= 2.3")
+end
diff --git a/ext/json/lib/json.rb b/ext/json/lib/json.rb
new file mode 100644
index 0000000000..1e64bfcb1a
--- /dev/null
+++ b/ext/json/lib/json.rb
@@ -0,0 +1,583 @@
+#frozen_string_literal: false
+require 'json/common'
+
+##
+# = JavaScript \Object Notation (\JSON)
+#
+# \JSON is a lightweight data-interchange format.
+#
+# A \JSON value is one of the following:
+# - Double-quoted text: <tt>"foo"</tt>.
+# - Number: +1+, +1.0+, +2.0e2+.
+# - Boolean: +true+, +false+.
+# - Null: +null+.
+# - \Array: an ordered list of values, enclosed by square brackets:
+# ["foo", 1, 1.0, 2.0e2, true, false, null]
+#
+# - \Object: a collection of name/value pairs, enclosed by curly braces;
+# each name is double-quoted text;
+# the values may be any \JSON values:
+# {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
+#
+# A \JSON array or object may contain nested arrays, objects, and scalars
+# to any depth:
+# {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}
+# [{"foo": 0, "bar": 1}, ["baz", 2]]
+#
+# == Using \Module \JSON
+#
+# To make module \JSON available in your code, begin with:
+# require 'json'
+#
+# All examples here assume that this has been done.
+#
+# === Parsing \JSON
+#
+# You can parse a \String containing \JSON data using
+# either of two methods:
+# - <tt>JSON.parse(source, opts)</tt>
+# - <tt>JSON.parse!(source, opts)</tt>
+#
+# where
+# - +source+ is a Ruby object.
+# - +opts+ is a \Hash object containing options
+# that control both input allowed and output formatting.
+#
+# The difference between the two methods
+# is that JSON.parse! omits some checks
+# and may not be safe for some +source+ data;
+# use it only for data from trusted sources.
+# Use the safer method JSON.parse for less trusted sources.
+#
+# ==== Parsing \JSON Arrays
+#
+# When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array:
+# json = '["foo", 1, 1.0, 2.0e2, true, false, null]'
+# ruby = JSON.parse(json)
+# ruby # => ["foo", 1, 1.0, 200.0, true, false, nil]
+# ruby.class # => Array
+#
+# The \JSON array may contain nested arrays, objects, and scalars
+# to any depth:
+# json = '[{"foo": 0, "bar": 1}, ["baz", 2]]'
+# JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
+#
+# ==== Parsing \JSON \Objects
+#
+# When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash:
+# json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}'
+# ruby = JSON.parse(json)
+# ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil}
+# ruby.class # => Hash
+#
+# The \JSON object may contain nested arrays, objects, and scalars
+# to any depth:
+# json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}'
+# JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
+#
+# ==== Parsing \JSON Scalars
+#
+# When the source is a \JSON scalar (not an array or object),
+# JSON.parse returns a Ruby scalar.
+#
+# \String:
+# ruby = JSON.parse('"foo"')
+# ruby # => 'foo'
+# ruby.class # => String
+# \Integer:
+# ruby = JSON.parse('1')
+# ruby # => 1
+# ruby.class # => Integer
+# \Float:
+# ruby = JSON.parse('1.0')
+# ruby # => 1.0
+# ruby.class # => Float
+# ruby = JSON.parse('2.0e2')
+# ruby # => 200
+# ruby.class # => Float
+# Boolean:
+# ruby = JSON.parse('true')
+# ruby # => true
+# ruby.class # => TrueClass
+# ruby = JSON.parse('false')
+# ruby # => false
+# ruby.class # => FalseClass
+# Null:
+# ruby = JSON.parse('null')
+# ruby # => nil
+# ruby.class # => NilClass
+#
+# ==== Parsing Options
+#
+# ====== Input Options
+#
+# Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed;
+# defaults to +100+; specify +false+ to disable depth checking.
+#
+# With the default, +false+:
+# source = '[0, [1, [2, [3]]]]'
+# ruby = JSON.parse(source)
+# ruby # => [0, [1, [2, [3]]]]
+# Too deep:
+# # Raises JSON::NestingError (nesting of 2 is too deep):
+# JSON.parse(source, {max_nesting: 1})
+# Bad value:
+# # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
+# JSON.parse(source, {max_nesting: :foo})
+#
+# ---
+#
+# Option +allow_nan+ (boolean) specifies whether to allow
+# NaN, Infinity, and MinusInfinity in +source+;
+# defaults to +false+.
+#
+# With the default, +false+:
+# # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
+# JSON.parse('[NaN]')
+# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
+# JSON.parse('[Infinity]')
+# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
+# JSON.parse('[-Infinity]')
+# Allow:
+# source = '[NaN, Infinity, -Infinity]'
+# ruby = JSON.parse(source, {allow_nan: true})
+# ruby # => [NaN, Infinity, -Infinity]
+#
+# ====== Output Options
+#
+# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
+# should be Symbols;
+# defaults to +false+ (use Strings).
+#
+# With the default, +false+:
+# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
+# ruby = JSON.parse(source)
+# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
+# Use Symbols:
+# ruby = JSON.parse(source, {symbolize_names: true})
+# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
+#
+# ---
+#
+# Option +object_class+ (\Class) specifies the Ruby class to be used
+# for each \JSON object;
+# defaults to \Hash.
+#
+# With the default, \Hash:
+# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
+# ruby = JSON.parse(source)
+# ruby.class # => Hash
+# Use class \OpenStruct:
+# ruby = JSON.parse(source, {object_class: OpenStruct})
+# ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
+#
+# ---
+#
+# Option +array_class+ (\Class) specifies the Ruby class to be used
+# for each \JSON array;
+# defaults to \Array.
+#
+# With the default, \Array:
+# source = '["foo", 1.0, true, false, null]'
+# ruby = JSON.parse(source)
+# ruby.class # => Array
+# Use class \Set:
+# ruby = JSON.parse(source, {array_class: Set})
+# ruby # => #<Set: {"foo", 1.0, true, false, nil}>
+#
+# ---
+#
+# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
+# See {\JSON Additions}[#module-JSON-label-JSON+Additions].
+#
+# === Generating \JSON
+#
+# To generate a Ruby \String containing \JSON data,
+# use method <tt>JSON.generate(source, opts)</tt>, where
+# - +source+ is a Ruby object.
+# - +opts+ is a \Hash object containing options
+# that control both input allowed and output formatting.
+#
+# ==== Generating \JSON from Arrays
+#
+# When the source is a Ruby \Array, JSON.generate returns
+# a \String containing a \JSON array:
+# ruby = [0, 's', :foo]
+# json = JSON.generate(ruby)
+# json # => '[0,"s","foo"]'
+#
+# The Ruby \Array array may contain nested arrays, hashes, and scalars
+# to any depth:
+# ruby = [0, [1, 2], {foo: 3, bar: 4}]
+# json = JSON.generate(ruby)
+# json # => '[0,[1,2],{"foo":3,"bar":4}]'
+#
+# ==== Generating \JSON from Hashes
+#
+# When the source is a Ruby \Hash, JSON.generate returns
+# a \String containing a \JSON object:
+# ruby = {foo: 0, bar: 's', baz: :bat}
+# json = JSON.generate(ruby)
+# json # => '{"foo":0,"bar":"s","baz":"bat"}'
+#
+# The Ruby \Hash array may contain nested arrays, hashes, and scalars
+# to any depth:
+# ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
+# json = JSON.generate(ruby)
+# json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
+#
+# ==== Generating \JSON from Other Objects
+#
+# When the source is neither an \Array nor a \Hash,
+# the generated \JSON data depends on the class of the source.
+#
+# When the source is a Ruby \Integer or \Float, JSON.generate returns
+# a \String containing a \JSON number:
+# JSON.generate(42) # => '42'
+# JSON.generate(0.42) # => '0.42'
+#
+# When the source is a Ruby \String, JSON.generate returns
+# a \String containing a \JSON string (with double-quotes):
+# JSON.generate('A string') # => '"A string"'
+#
+# When the source is +true+, +false+ or +nil+, JSON.generate returns
+# a \String containing the corresponding \JSON token:
+# JSON.generate(true) # => 'true'
+# JSON.generate(false) # => 'false'
+# JSON.generate(nil) # => 'null'
+#
+# When the source is none of the above, JSON.generate returns
+# a \String containing a \JSON string representation of the source:
+# JSON.generate(:foo) # => '"foo"'
+# JSON.generate(Complex(0, 0)) # => '"0+0i"'
+# JSON.generate(Dir.new('.')) # => '"#<Dir>"'
+#
+# ==== Generating Options
+#
+# ====== Input Options
+#
+# Option +allow_nan+ (boolean) specifies whether
+# +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
+# defaults to +false+.
+#
+# With the default, +false+:
+# # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
+# JSON.generate(JSON::NaN)
+# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
+# JSON.generate(JSON::Infinity)
+# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
+# JSON.generate(JSON::MinusInfinity)
+#
+# Allow:
+# ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
+# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
+#
+# ---
+#
+# Option +max_nesting+ (\Integer) specifies the maximum nesting depth
+# in +obj+; defaults to +100+.
+#
+# With the default, +100+:
+# obj = [[[[[[0]]]]]]
+# JSON.generate(obj) # => '[[[[[[0]]]]]]'
+#
+# Too deep:
+# # Raises JSON::NestingError (nesting of 2 is too deep):
+# JSON.generate(obj, max_nesting: 2)
+#
+# ====== Output Options
+#
+# The default formatting options generate the most compact
+# \JSON data, all on one line and with no whitespace.
+#
+# You can use these formatting options to generate
+# \JSON data in a more open format, using whitespace.
+# See also JSON.pretty_generate.
+#
+# - Option +array_nl+ (\String) specifies a string (usually a newline)
+# to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
+# - Option +object_nl+ (\String) specifies a string (usually a newline)
+# to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
+# - Option +indent+ (\String) specifies the string (usually spaces) to be
+# used for indentation; defaults to the empty \String, <tt>''</tt>;
+# defaults to the empty \String, <tt>''</tt>;
+# has no effect unless options +array_nl+ or +object_nl+ specify newlines.
+# - Option +space+ (\String) specifies a string (usually a space) to be
+# inserted after the colon in each \JSON object's pair;
+# defaults to the empty \String, <tt>''</tt>.
+# - Option +space_before+ (\String) specifies a string (usually a space) to be
+# inserted before the colon in each \JSON object's pair;
+# defaults to the empty \String, <tt>''</tt>.
+#
+# In this example, +obj+ is used first to generate the shortest
+# \JSON data (no whitespace), then again with all formatting options
+# specified:
+#
+# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
+# json = JSON.generate(obj)
+# puts 'Compact:', json
+# opts = {
+# array_nl: "\n",
+# object_nl: "\n",
+# indent: ' ',
+# space_before: ' ',
+# space: ' '
+# }
+# puts 'Open:', JSON.generate(obj, opts)
+#
+# Output:
+# Compact:
+# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
+# Open:
+# {
+# "foo" : [
+# "bar",
+# "baz"
+# ],
+# "bat" : {
+# "bam" : 0,
+# "bad" : 1
+# }
+# }
+#
+# == \JSON Additions
+#
+# When you "round trip" a non-\String object from Ruby to \JSON and back,
+# you have a new \String, instead of the object you began with:
+# ruby0 = Range.new(0, 2)
+# json = JSON.generate(ruby0)
+# json # => '0..2"'
+# ruby1 = JSON.parse(json)
+# ruby1 # => '0..2'
+# ruby1.class # => String
+#
+# You can use \JSON _additions_ to preserve the original object.
+# The addition is an extension of a ruby class, so that:
+# - \JSON.generate stores more information in the \JSON string.
+# - \JSON.parse, called with option +create_additions+,
+# uses that information to create a proper Ruby object.
+#
+# This example shows a \Range being generated into \JSON
+# and parsed back into Ruby, both without and with
+# the addition for \Range:
+# ruby = Range.new(0, 2)
+# # This passage does not use the addition for Range.
+# json0 = JSON.generate(ruby)
+# ruby0 = JSON.parse(json0)
+# # This passage uses the addition for Range.
+# require 'json/add/range'
+# json1 = JSON.generate(ruby)
+# ruby1 = JSON.parse(json1, create_additions: true)
+# # Make a nice display.
+# display = <<EOT
+# Generated JSON:
+# Without addition: #{json0} (#{json0.class})
+# With addition: #{json1} (#{json1.class})
+# Parsed JSON:
+# Without addition: #{ruby0.inspect} (#{ruby0.class})
+# With addition: #{ruby1.inspect} (#{ruby1.class})
+# EOT
+# puts display
+#
+# This output shows the different results:
+# Generated JSON:
+# Without addition: "0..2" (String)
+# With addition: {"json_class":"Range","a":[0,2,false]} (String)
+# Parsed JSON:
+# Without addition: "0..2" (String)
+# With addition: 0..2 (Range)
+#
+# The \JSON module includes additions for certain classes.
+# You can also craft custom additions.
+# See {Custom \JSON Additions}[#module-JSON-label-Custom+JSON+Additions].
+#
+# === Built-in Additions
+#
+# The \JSON module includes additions for certain classes.
+# To use an addition, +require+ its source:
+# - BigDecimal: <tt>require 'json/add/bigdecimal'</tt>
+# - Complex: <tt>require 'json/add/complex'</tt>
+# - Date: <tt>require 'json/add/date'</tt>
+# - DateTime: <tt>require 'json/add/date_time'</tt>
+# - Exception: <tt>require 'json/add/exception'</tt>
+# - OpenStruct: <tt>require 'json/add/ostruct'</tt>
+# - Range: <tt>require 'json/add/range'</tt>
+# - Rational: <tt>require 'json/add/rational'</tt>
+# - Regexp: <tt>require 'json/add/regexp'</tt>
+# - Set: <tt>require 'json/add/set'</tt>
+# - Struct: <tt>require 'json/add/struct'</tt>
+# - Symbol: <tt>require 'json/add/symbol'</tt>
+# - Time: <tt>require 'json/add/time'</tt>
+#
+# To reduce punctuation clutter, the examples below
+# show the generated \JSON via +puts+, rather than the usual +inspect+,
+#
+# \BigDecimal:
+# require 'json/add/bigdecimal'
+# ruby0 = BigDecimal(0) # 0.0
+# json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"}
+# ruby1 = JSON.parse(json, create_additions: true) # 0.0
+# ruby1.class # => BigDecimal
+#
+# \Complex:
+# require 'json/add/complex'
+# ruby0 = Complex(1+0i) # 1+0i
+# json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0}
+# ruby1 = JSON.parse(json, create_additions: true) # 1+0i
+# ruby1.class # Complex
+#
+# \Date:
+# require 'json/add/date'
+# ruby0 = Date.today # 2020-05-02
+# json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0}
+# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02
+# ruby1.class # Date
+#
+# \DateTime:
+# require 'json/add/date_time'
+# ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00
+# json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0}
+# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00
+# ruby1.class # DateTime
+#
+# \Exception (and its subclasses including \RuntimeError):
+# require 'json/add/exception'
+# ruby0 = Exception.new('A message') # A message
+# json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null}
+# ruby1 = JSON.parse(json, create_additions: true) # A message
+# ruby1.class # Exception
+# ruby0 = RuntimeError.new('Another message') # Another message
+# json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null}
+# ruby1 = JSON.parse(json, create_additions: true) # Another message
+# ruby1.class # RuntimeError
+#
+# \OpenStruct:
+# require 'json/add/ostruct'
+# ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby">
+# json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}}
+# ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby">
+# ruby1.class # OpenStruct
+#
+# \Range:
+# require 'json/add/range'
+# ruby0 = Range.new(0, 2) # 0..2
+# json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]}
+# ruby1 = JSON.parse(json, create_additions: true) # 0..2
+# ruby1.class # Range
+#
+# \Rational:
+# require 'json/add/rational'
+# ruby0 = Rational(1, 3) # 1/3
+# json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3}
+# ruby1 = JSON.parse(json, create_additions: true) # 1/3
+# ruby1.class # Rational
+#
+# \Regexp:
+# require 'json/add/regexp'
+# ruby0 = Regexp.new('foo') # (?-mix:foo)
+# json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"}
+# ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo)
+# ruby1.class # Regexp
+#
+# \Set:
+# require 'json/add/set'
+# ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}>
+# json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]}
+# ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}>
+# ruby1.class # Set
+#
+# \Struct:
+# require 'json/add/struct'
+# Customer = Struct.new(:name, :address) # Customer
+# ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main">
+# json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]}
+# ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main">
+# ruby1.class # Customer
+#
+# \Symbol:
+# require 'json/add/symbol'
+# ruby0 = :foo # foo
+# json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"}
+# ruby1 = JSON.parse(json, create_additions: true) # foo
+# ruby1.class # Symbol
+#
+# \Time:
+# require 'json/add/time'
+# ruby0 = Time.now # 2020-05-02 11:28:26 -0500
+# json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000}
+# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500
+# ruby1.class # Time
+#
+#
+# === Custom \JSON Additions
+#
+# In addition to the \JSON additions provided,
+# you can craft \JSON additions of your own,
+# either for Ruby built-in classes or for user-defined classes.
+#
+# Here's a user-defined class +Foo+:
+# class Foo
+# attr_accessor :bar, :baz
+# def initialize(bar, baz)
+# self.bar = bar
+# self.baz = baz
+# end
+# end
+#
+# Here's the \JSON addition for it:
+# # Extend class Foo with JSON addition.
+# class Foo
+# # Serialize Foo object with its class name and arguments
+# def to_json(*args)
+# {
+# JSON.create_id => self.class.name,
+# 'a' => [ bar, baz ]
+# }.to_json(*args)
+# end
+# # Deserialize JSON string by constructing new Foo object with arguments.
+# def self.json_create(object)
+# new(*object['a'])
+# end
+# end
+#
+# Demonstration:
+# require 'json'
+# # This Foo object has no custom addition.
+# foo0 = Foo.new(0, 1)
+# json0 = JSON.generate(foo0)
+# obj0 = JSON.parse(json0)
+# # Lood the custom addition.
+# require_relative 'foo_addition'
+# # This foo has the custom addition.
+# foo1 = Foo.new(0, 1)
+# json1 = JSON.generate(foo1)
+# obj1 = JSON.parse(json1, create_additions: true)
+# # Make a nice display.
+# display = <<EOT
+# Generated JSON:
+# Without custom addition: #{json0} (#{json0.class})
+# With custom addition: #{json1} (#{json1.class})
+# Parsed JSON:
+# Without custom addition: #{obj0.inspect} (#{obj0.class})
+# With custom addition: #{obj1.inspect} (#{obj1.class})
+# EOT
+# puts display
+#
+# Output:
+#
+# Generated JSON:
+# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
+# With custom addition: {"json_class":"Foo","a":[0,1]} (String)
+# Parsed JSON:
+# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
+# With custom addition: #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)
+#
+module JSON
+ require 'json/version'
+
+ begin
+ require 'json/ext'
+ rescue LoadError
+ require 'json/pure'
+ end
+end
diff --git a/ext/json/lib/json/add/bigdecimal.rb b/ext/json/lib/json/add/bigdecimal.rb
new file mode 100644
index 0000000000..c8b4f567cb
--- /dev/null
+++ b/ext/json/lib/json/add/bigdecimal.rb
@@ -0,0 +1,29 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+defined?(::BigDecimal) or require 'bigdecimal'
+
+class BigDecimal
+ # Import a JSON Marshalled object.
+ #
+ # method used for JSON marshalling support.
+ def self.json_create(object)
+ BigDecimal._load object['b']
+ end
+
+ # Marshal the object to JSON.
+ #
+ # method used for JSON marshalling support.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'b' => _dump,
+ }
+ end
+
+ # return the JSON value
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/complex.rb b/ext/json/lib/json/add/complex.rb
new file mode 100644
index 0000000000..e63e29fd22
--- /dev/null
+++ b/ext/json/lib/json/add/complex.rb
@@ -0,0 +1,28 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Complex
+
+ # Deserializes JSON string by converting Real value <tt>r</tt>, imaginary
+ # value <tt>i</tt>, to a Complex object.
+ def self.json_create(object)
+ Complex(object['r'], object['i'])
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'r' => real,
+ 'i' => imag,
+ }
+ end
+
+ # Stores class name (Complex) along with real value <tt>r</tt> and imaginary value <tt>i</tt> as JSON string
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/core.rb b/ext/json/lib/json/add/core.rb
new file mode 100644
index 0000000000..bfb017c460
--- /dev/null
+++ b/ext/json/lib/json/add/core.rb
@@ -0,0 +1,12 @@
+#frozen_string_literal: false
+# This file requires the implementations of ruby core's custom objects for
+# serialisation/deserialisation.
+
+require 'json/add/date'
+require 'json/add/date_time'
+require 'json/add/exception'
+require 'json/add/range'
+require 'json/add/regexp'
+require 'json/add/struct'
+require 'json/add/symbol'
+require 'json/add/time'
diff --git a/ext/json/lib/json/add/date.rb b/ext/json/lib/json/add/date.rb
new file mode 100644
index 0000000000..25523561a5
--- /dev/null
+++ b/ext/json/lib/json/add/date.rb
@@ -0,0 +1,34 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+require 'date'
+
+class Date
+
+ # Deserializes JSON string by converting Julian year <tt>y</tt>, month
+ # <tt>m</tt>, day <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> to Date.
+ def self.json_create(object)
+ civil(*object.values_at('y', 'm', 'd', 'sg'))
+ end
+
+ alias start sg unless method_defined?(:start)
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'y' => year,
+ 'm' => month,
+ 'd' => day,
+ 'sg' => start,
+ }
+ end
+
+ # Stores class name (Date) with Julian year <tt>y</tt>, month <tt>m</tt>, day
+ # <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/date_time.rb b/ext/json/lib/json/add/date_time.rb
new file mode 100644
index 0000000000..38b0e86ab8
--- /dev/null
+++ b/ext/json/lib/json/add/date_time.rb
@@ -0,0 +1,50 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+require 'date'
+
+class DateTime
+
+ # Deserializes JSON string by converting year <tt>y</tt>, month <tt>m</tt>,
+ # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>,
+ # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> to DateTime.
+ def self.json_create(object)
+ args = object.values_at('y', 'm', 'd', 'H', 'M', 'S')
+ of_a, of_b = object['of'].split('/')
+ if of_b and of_b != '0'
+ args << Rational(of_a.to_i, of_b.to_i)
+ else
+ args << of_a
+ end
+ args << object['sg']
+ civil(*args)
+ end
+
+ alias start sg unless method_defined?(:start)
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'y' => year,
+ 'm' => month,
+ 'd' => day,
+ 'H' => hour,
+ 'M' => min,
+ 'S' => sec,
+ 'of' => offset.to_s,
+ 'sg' => start,
+ }
+ end
+
+ # Stores class name (DateTime) with Julian year <tt>y</tt>, month <tt>m</tt>,
+ # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>,
+ # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
+
+
diff --git a/ext/json/lib/json/add/exception.rb b/ext/json/lib/json/add/exception.rb
new file mode 100644
index 0000000000..a107e5b3c4
--- /dev/null
+++ b/ext/json/lib/json/add/exception.rb
@@ -0,0 +1,31 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Exception
+
+ # Deserializes JSON string by constructing new Exception object with message
+ # <tt>m</tt> and backtrace <tt>b</tt> serialized with <tt>to_json</tt>
+ def self.json_create(object)
+ result = new(object['m'])
+ result.set_backtrace object['b']
+ result
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'm' => message,
+ 'b' => backtrace,
+ }
+ end
+
+ # Stores class name (Exception) with message <tt>m</tt> and backtrace array
+ # <tt>b</tt> as JSON string
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/ostruct.rb b/ext/json/lib/json/add/ostruct.rb
new file mode 100644
index 0000000000..686cf0025d
--- /dev/null
+++ b/ext/json/lib/json/add/ostruct.rb
@@ -0,0 +1,31 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+require 'ostruct'
+
+class OpenStruct
+
+ # Deserializes JSON string by constructing new Struct object with values
+ # <tt>t</tt> serialized by <tt>to_json</tt>.
+ def self.json_create(object)
+ new(object['t'] || object[:t])
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ klass = self.class.name
+ klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
+ {
+ JSON.create_id => klass,
+ 't' => table,
+ }
+ end
+
+ # Stores class name (OpenStruct) with this struct's values <tt>t</tt> as a
+ # JSON string.
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/range.rb b/ext/json/lib/json/add/range.rb
new file mode 100644
index 0000000000..93529fb1c4
--- /dev/null
+++ b/ext/json/lib/json/add/range.rb
@@ -0,0 +1,29 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Range
+
+ # Deserializes JSON string by constructing new Range object with arguments
+ # <tt>a</tt> serialized by <tt>to_json</tt>.
+ def self.json_create(object)
+ new(*object['a'])
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'a' => [ first, last, exclude_end? ]
+ }
+ end
+
+ # Stores class name (Range) with JSON array of arguments <tt>a</tt> which
+ # include <tt>first</tt> (integer), <tt>last</tt> (integer), and
+ # <tt>exclude_end?</tt> (boolean) as JSON string.
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/rational.rb b/ext/json/lib/json/add/rational.rb
new file mode 100644
index 0000000000..f776226046
--- /dev/null
+++ b/ext/json/lib/json/add/rational.rb
@@ -0,0 +1,27 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Rational
+ # Deserializes JSON string by converting numerator value <tt>n</tt>,
+ # denominator value <tt>d</tt>, to a Rational object.
+ def self.json_create(object)
+ Rational(object['n'], object['d'])
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'n' => numerator,
+ 'd' => denominator,
+ }
+ end
+
+ # Stores class name (Rational) along with numerator value <tt>n</tt> and denominator value <tt>d</tt> as JSON string
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/regexp.rb b/ext/json/lib/json/add/regexp.rb
new file mode 100644
index 0000000000..39d69fede7
--- /dev/null
+++ b/ext/json/lib/json/add/regexp.rb
@@ -0,0 +1,30 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Regexp
+
+ # Deserializes JSON string by constructing new Regexp object with source
+ # <tt>s</tt> (Regexp or String) and options <tt>o</tt> serialized by
+ # <tt>to_json</tt>
+ def self.json_create(object)
+ new(object['s'], object['o'])
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'o' => options,
+ 's' => source,
+ }
+ end
+
+ # Stores class name (Regexp) with options <tt>o</tt> and source <tt>s</tt>
+ # (Regexp or String) as JSON string
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/set.rb b/ext/json/lib/json/add/set.rb
new file mode 100644
index 0000000000..71e2a0ac8b
--- /dev/null
+++ b/ext/json/lib/json/add/set.rb
@@ -0,0 +1,29 @@
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+defined?(::Set) or require 'set'
+
+class Set
+ # Import a JSON Marshalled object.
+ #
+ # method used for JSON marshalling support.
+ def self.json_create(object)
+ new object['a']
+ end
+
+ # Marshal the object to JSON.
+ #
+ # method used for JSON marshalling support.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'a' => to_a,
+ }
+ end
+
+ # return the JSON value
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
+
diff --git a/ext/json/lib/json/add/struct.rb b/ext/json/lib/json/add/struct.rb
new file mode 100644
index 0000000000..e8395ed42f
--- /dev/null
+++ b/ext/json/lib/json/add/struct.rb
@@ -0,0 +1,30 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Struct
+
+ # Deserializes JSON string by constructing new Struct object with values
+ # <tt>v</tt> serialized by <tt>to_json</tt>.
+ def self.json_create(object)
+ new(*object['v'])
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ klass = self.class.name
+ klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
+ {
+ JSON.create_id => klass,
+ 'v' => values,
+ }
+ end
+
+ # Stores class name (Struct) with Struct values <tt>v</tt> as a JSON string.
+ # Only named structs are supported.
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/add/symbol.rb b/ext/json/lib/json/add/symbol.rb
new file mode 100644
index 0000000000..74b13a423f
--- /dev/null
+++ b/ext/json/lib/json/add/symbol.rb
@@ -0,0 +1,25 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Symbol
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 's' => to_s,
+ }
+ end
+
+ # Stores class name (Symbol) with String representation of Symbol as a JSON string.
+ def to_json(*a)
+ as_json.to_json(*a)
+ end
+
+ # Deserializes JSON string by converting the <tt>string</tt> value stored in the object to a Symbol
+ def self.json_create(o)
+ o['s'].to_sym
+ end
+end
diff --git a/ext/json/lib/json/add/time.rb b/ext/json/lib/json/add/time.rb
new file mode 100644
index 0000000000..b73acc4086
--- /dev/null
+++ b/ext/json/lib/json/add/time.rb
@@ -0,0 +1,38 @@
+#frozen_string_literal: false
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Time
+
+ # Deserializes JSON string by converting time since epoch to Time
+ def self.json_create(object)
+ if usec = object.delete('u') # used to be tv_usec -> tv_nsec
+ object['n'] = usec * 1000
+ end
+ if method_defined?(:tv_nsec)
+ at(object['s'], Rational(object['n'], 1000))
+ else
+ at(object['s'], object['n'] / 1000)
+ end
+ end
+
+ # Returns a hash, that will be turned into a JSON object and represent this
+ # object.
+ def as_json(*)
+ nanoseconds = [ tv_usec * 1000 ]
+ respond_to?(:tv_nsec) and nanoseconds << tv_nsec
+ nanoseconds = nanoseconds.max
+ {
+ JSON.create_id => self.class.name,
+ 's' => tv_sec,
+ 'n' => nanoseconds,
+ }
+ end
+
+ # Stores class name (Time) with number of seconds since epoch and number of
+ # microseconds for Time as JSON string
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb
new file mode 100644
index 0000000000..ea46896fcc
--- /dev/null
+++ b/ext/json/lib/json/common.rb
@@ -0,0 +1,703 @@
+#frozen_string_literal: false
+require 'json/version'
+require 'json/generic_object'
+
+module JSON
+ class << self
+ # :call-seq:
+ # JSON[object] -> new_array or new_string
+ #
+ # If +object+ is a \String,
+ # calls JSON.parse with +object+ and +opts+ (see method #parse):
+ # json = '[0, 1, null]'
+ # JSON[json]# => [0, 1, nil]
+ #
+ # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
+ # ruby = [0, 1, nil]
+ # JSON[ruby] # => '[0,1,null]'
+ def [](object, opts = {})
+ if object.respond_to? :to_str
+ JSON.parse(object.to_str, opts)
+ else
+ JSON.generate(object, opts)
+ end
+ end
+
+ # Returns the JSON parser class that is used by JSON. This is either
+ # JSON::Ext::Parser or JSON::Pure::Parser:
+ # JSON.parser # => JSON::Ext::Parser
+ attr_reader :parser
+
+ # Set the JSON parser class _parser_ to be used by JSON.
+ def parser=(parser) # :nodoc:
+ @parser = parser
+ remove_const :Parser if const_defined?(:Parser, false)
+ const_set :Parser, parser
+ end
+
+ # Return the constant located at _path_. The format of _path_ has to be
+ # either ::A::B::C or A::B::C. In any case, A has to be located at the top
+ # level (absolute namespace path?). If there doesn't exist a constant at
+ # the given path, an ArgumentError is raised.
+ def deep_const_get(path) # :nodoc:
+ path.to_s.split(/::/).inject(Object) do |p, c|
+ case
+ when c.empty? then p
+ when p.const_defined?(c, true) then p.const_get(c)
+ else
+ begin
+ p.const_missing(c)
+ rescue NameError => e
+ raise ArgumentError, "can't get const #{path}: #{e}"
+ end
+ end
+ end
+ end
+
+ # Set the module _generator_ to be used by JSON.
+ def generator=(generator) # :nodoc:
+ old, $VERBOSE = $VERBOSE, nil
+ @generator = generator
+ generator_methods = generator::GeneratorMethods
+ for const in generator_methods.constants
+ klass = deep_const_get(const)
+ modul = generator_methods.const_get(const)
+ klass.class_eval do
+ instance_methods(false).each do |m|
+ m.to_s == 'to_json' and remove_method m
+ end
+ include modul
+ end
+ end
+ self.state = generator::State
+ const_set :State, self.state
+ const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
+ const_set :FAST_STATE_PROTOTYPE, create_fast_state
+ const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state
+ ensure
+ $VERBOSE = old
+ end
+
+ def create_fast_state
+ State.new(
+ :indent => '',
+ :space => '',
+ :object_nl => "",
+ :array_nl => "",
+ :max_nesting => false
+ )
+ end
+
+ def create_pretty_state
+ State.new(
+ :indent => ' ',
+ :space => ' ',
+ :object_nl => "\n",
+ :array_nl => "\n"
+ )
+ end
+
+ # Returns the JSON generator module that is used by JSON. This is
+ # either JSON::Ext::Generator or JSON::Pure::Generator:
+ # JSON.generator # => JSON::Ext::Generator
+ attr_reader :generator
+
+ # Sets or Returns the JSON generator state class that is used by JSON. This is
+ # either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
+ # JSON.state # => JSON::Ext::Generator::State
+ attr_accessor :state
+ end
+
+ DEFAULT_CREATE_ID = 'json_class'.freeze
+ private_constant :DEFAULT_CREATE_ID
+
+ CREATE_ID_TLS_KEY = "JSON.create_id".freeze
+ private_constant :CREATE_ID_TLS_KEY
+
+ # Sets create identifier, which is used to decide if the _json_create_
+ # hook of a class should be called; initial value is +json_class+:
+ # JSON.create_id # => 'json_class'
+ def self.create_id=(new_value)
+ Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
+ end
+
+ # Returns the current create identifier.
+ # See also JSON.create_id=.
+ def self.create_id
+ Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
+ end
+
+ NaN = 0.0/0
+
+ Infinity = 1.0/0
+
+ MinusInfinity = -Infinity
+
+ # The base exception for JSON errors.
+ class JSONError < StandardError
+ def self.wrap(exception)
+ obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
+ obj.set_backtrace exception.backtrace
+ obj
+ end
+ end
+
+ # This exception is raised if a parser error occurs.
+ class ParserError < JSONError; end
+
+ # This exception is raised if the nesting of parsed data structures is too
+ # deep.
+ class NestingError < ParserError; end
+
+ # :stopdoc:
+ class CircularDatastructure < NestingError; end
+ # :startdoc:
+
+ # This exception is raised if a generator or unparser error occurs.
+ class GeneratorError < JSONError; end
+ # For backwards compatibility
+ UnparserError = GeneratorError # :nodoc:
+
+ # This exception is raised if the required unicode support is missing on the
+ # system. Usually this means that the iconv library is not installed.
+ class MissingUnicodeSupport < JSONError; end
+
+ module_function
+
+ # :call-seq:
+ # JSON.parse(source, opts) -> object
+ #
+ # Returns the Ruby objects created by parsing the given +source+.
+ #
+ # Argument +source+ contains the \String to be parsed.
+ #
+ # Argument +opts+, if given, contains a \Hash of options for the parsing.
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ #
+ # ---
+ #
+ # When +source+ is a \JSON array, returns a Ruby \Array:
+ # source = '["foo", 1.0, true, false, null]'
+ # ruby = JSON.parse(source)
+ # ruby # => ["foo", 1.0, true, false, nil]
+ # ruby.class # => Array
+ #
+ # When +source+ is a \JSON object, returns a Ruby \Hash:
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
+ # ruby = JSON.parse(source)
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
+ # ruby.class # => Hash
+ #
+ # For examples of parsing for all \JSON data types, see
+ # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
+ #
+ # Parses nested JSON objects:
+ # source = <<-EOT
+ # {
+ # "name": "Dave",
+ # "age" :40,
+ # "hats": [
+ # "Cattleman's",
+ # "Panama",
+ # "Tophat"
+ # ]
+ # }
+ # EOT
+ # ruby = JSON.parse(source)
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # ---
+ #
+ # Raises an exception if +source+ is not valid JSON:
+ # # Raises JSON::ParserError (783: unexpected token at ''):
+ # JSON.parse('')
+ #
+ def parse(source, opts = {})
+ Parser.new(source, **(opts||{})).parse
+ end
+
+ # :call-seq:
+ # JSON.parse!(source, opts) -> object
+ #
+ # Calls
+ # parse(source, opts)
+ # with +source+ and possibly modified +opts+.
+ #
+ # Differences from JSON.parse:
+ # - Option +max_nesting+, if not provided, defaults to +false+,
+ # which disables checking for nesting depth.
+ # - Option +allow_nan+, if not provided, defaults to +true+.
+ def parse!(source, opts = {})
+ opts = {
+ :max_nesting => false,
+ :allow_nan => true
+ }.merge(opts)
+ Parser.new(source, **(opts||{})).parse
+ end
+
+ # :call-seq:
+ # JSON.load_file(path, opts={}) -> object
+ #
+ # Calls:
+ # parse(File.read(path), opts)
+ #
+ # See method #parse.
+ def load_file(filespec, opts = {})
+ parse(File.read(filespec), opts)
+ end
+
+ # :call-seq:
+ # JSON.load_file!(path, opts = {})
+ #
+ # Calls:
+ # JSON.parse!(File.read(path, opts))
+ #
+ # See method #parse!
+ def load_file!(filespec, opts = {})
+ parse!(File.read(filespec), opts)
+ end
+
+ # :call-seq:
+ # JSON.generate(obj, opts = nil) -> new_string
+ #
+ # Returns a \String containing the generated \JSON data.
+ #
+ # See also JSON.fast_generate, JSON.pretty_generate.
+ #
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
+ #
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
+ # See {Generating Options}[#module-JSON-label-Generating+Options].
+ #
+ # ---
+ #
+ # When +obj+ is an \Array, returns a \String containing a \JSON array:
+ # obj = ["foo", 1.0, true, false, nil]
+ # json = JSON.generate(obj)
+ # json # => '["foo",1.0,true,false,null]'
+ #
+ # When +obj+ is a \Hash, returns a \String containing a \JSON object:
+ # obj = {foo: 0, bar: 's', baz: :bat}
+ # json = JSON.generate(obj)
+ # json # => '{"foo":0,"bar":"s","baz":"bat"}'
+ #
+ # For examples of generating from other Ruby objects, see
+ # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
+ #
+ # ---
+ #
+ # Raises an exception if any formatting option is not a \String.
+ #
+ # Raises an exception if +obj+ contains circular references:
+ # a = []; b = []; a.push(b); b.push(a)
+ # # Raises JSON::NestingError (nesting of 100 is too deep):
+ # JSON.generate(a)
+ #
+ def generate(obj, opts = nil)
+ if State === opts
+ state, opts = opts, nil
+ else
+ state = State.new
+ end
+ if opts
+ if opts.respond_to? :to_hash
+ opts = opts.to_hash
+ elsif opts.respond_to? :to_h
+ opts = opts.to_h
+ else
+ raise TypeError, "can't convert #{opts.class} into Hash"
+ end
+ state = state.configure(opts)
+ end
+ state.generate(obj)
+ end
+
+ # :stopdoc:
+ # I want to deprecate these later, so I'll first be silent about them, and
+ # later delete them.
+ alias unparse generate
+ module_function :unparse
+ # :startdoc:
+
+ # :call-seq:
+ # JSON.fast_generate(obj, opts) -> new_string
+ #
+ # Arguments +obj+ and +opts+ here are the same as
+ # arguments +obj+ and +opts+ in JSON.generate.
+ #
+ # By default, generates \JSON data without checking
+ # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
+ #
+ # Raises an exception if +obj+ contains circular references:
+ # a = []; b = []; a.push(b); b.push(a)
+ # # Raises SystemStackError (stack level too deep):
+ # JSON.fast_generate(a)
+ def fast_generate(obj, opts = nil)
+ if State === opts
+ state, opts = opts, nil
+ else
+ state = JSON.create_fast_state
+ end
+ if opts
+ if opts.respond_to? :to_hash
+ opts = opts.to_hash
+ elsif opts.respond_to? :to_h
+ opts = opts.to_h
+ else
+ raise TypeError, "can't convert #{opts.class} into Hash"
+ end
+ state.configure(opts)
+ end
+ state.generate(obj)
+ end
+
+ # :stopdoc:
+ # I want to deprecate these later, so I'll first be silent about them, and later delete them.
+ alias fast_unparse fast_generate
+ module_function :fast_unparse
+ # :startdoc:
+
+ # :call-seq:
+ # JSON.pretty_generate(obj, opts = nil) -> new_string
+ #
+ # Arguments +obj+ and +opts+ here are the same as
+ # arguments +obj+ and +opts+ in JSON.generate.
+ #
+ # Default options are:
+ # {
+ # indent: ' ', # Two spaces
+ # space: ' ', # One space
+ # array_nl: "\n", # Newline
+ # object_nl: "\n" # Newline
+ # }
+ #
+ # Example:
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
+ # json = JSON.pretty_generate(obj)
+ # puts json
+ # Output:
+ # {
+ # "foo": [
+ # "bar",
+ # "baz"
+ # ],
+ # "bat": {
+ # "bam": 0,
+ # "bad": 1
+ # }
+ # }
+ #
+ def pretty_generate(obj, opts = nil)
+ if State === opts
+ state, opts = opts, nil
+ else
+ state = JSON.create_pretty_state
+ end
+ if opts
+ if opts.respond_to? :to_hash
+ opts = opts.to_hash
+ elsif opts.respond_to? :to_h
+ opts = opts.to_h
+ else
+ raise TypeError, "can't convert #{opts.class} into Hash"
+ end
+ state.configure(opts)
+ end
+ state.generate(obj)
+ end
+
+ # :stopdoc:
+ # I want to deprecate these later, so I'll first be silent about them, and later delete them.
+ alias pretty_unparse pretty_generate
+ module_function :pretty_unparse
+ # :startdoc:
+
+ class << self
+ # Sets or returns default options for the JSON.load method.
+ # Initially:
+ # opts = JSON.load_default_options
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
+ attr_accessor :load_default_options
+ end
+ self.load_default_options = {
+ :max_nesting => false,
+ :allow_nan => true,
+ :allow_blank => true,
+ :create_additions => true,
+ }
+
+ # :call-seq:
+ # JSON.load(source, proc = nil, options = {}) -> object
+ #
+ # Returns the Ruby objects created by parsing the given +source+.
+ #
+ # - Argument +source+ must be, or be convertible to, a \String:
+ # - If +source+ responds to instance method +to_str+,
+ # <tt>source.to_str</tt> becomes the source.
+ # - If +source+ responds to instance method +to_io+,
+ # <tt>source.to_io.read</tt> becomes the source.
+ # - If +source+ responds to instance method +read+,
+ # <tt>source.read</tt> becomes the source.
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
+ # - Option +allow_blank+ specifies a truthy value.
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
+ # - Otherwise, +source+ remains the source.
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
+ # It will be called recursively with each result (depth-first order).
+ # See details below.
+ # BEWARE: This method is meant to serialise data from trusted user input,
+ # like from your own database server or clients under your control, it could
+ # be dangerous to allow untrusted users to pass JSON sources into it.
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ # The default options can be changed via method JSON.load_default_options=.
+ #
+ # ---
+ #
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
+ # <tt>parse(source, opts)</tt>; see #parse.
+ #
+ # Source for following examples:
+ # source = <<-EOT
+ # {
+ # "name": "Dave",
+ # "age" :40,
+ # "hats": [
+ # "Cattleman's",
+ # "Panama",
+ # "Tophat"
+ # ]
+ # }
+ # EOT
+ #
+ # Load a \String:
+ # ruby = JSON.load(source)
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load an \IO object:
+ # require 'stringio'
+ # object = JSON.load(StringIO.new(source))
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load a \File object:
+ # path = 't.json'
+ # File.write(path, source)
+ # File.open(path) do |file|
+ # JSON.load(file)
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # ---
+ #
+ # When +proc+ is given:
+ # - Modifies +source+ as above.
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
+ # - Recursively calls <tt>proc(result)</tt>.
+ # - Returns the final result.
+ #
+ # Example:
+ # require 'json'
+ #
+ # # Some classes for the example.
+ # class Base
+ # def initialize(attributes)
+ # @attributes = attributes
+ # end
+ # end
+ # class User < Base; end
+ # class Account < Base; end
+ # class Admin < Base; end
+ # # The JSON source.
+ # json = <<-EOF
+ # {
+ # "users": [
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
+ # {"type": "User", "username": "john", "email": "john@example.com"}
+ # ],
+ # "accounts": [
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
+ # ],
+ # "admins": {"type": "Admin", "password": "0wn3d"}
+ # }
+ # EOF
+ # # Deserializer method.
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
+ # type = obj.is_a?(Hash) && obj["type"]
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
+ # end
+ # # Call to JSON.load
+ # ruby = JSON.load(json, proc {|obj|
+ # case obj
+ # when Hash
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
+ # when Array
+ # obj.map! {|v| deserialize_obj v }
+ # end
+ # })
+ # pp ruby
+ # Output:
+ # {"users"=>
+ # [#<User:0x00000000064c4c98
+ # @attributes=
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
+ # #<User:0x00000000064c4bd0
+ # @attributes=
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
+ # "accounts"=>
+ # [{"account"=>
+ # #<Account:0x00000000064c4928
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
+ # {"account"=>
+ # #<Account:0x00000000064c4680
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
+ # "admins"=>
+ # #<Admin:0x00000000064c41f8
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
+ #
+ def load(source, proc = nil, options = {})
+ opts = load_default_options.merge options
+ if source.respond_to? :to_str
+ source = source.to_str
+ elsif source.respond_to? :to_io
+ source = source.to_io.read
+ elsif source.respond_to?(:read)
+ source = source.read
+ end
+ if opts[:allow_blank] && (source.nil? || source.empty?)
+ source = 'null'
+ end
+ result = parse(source, opts)
+ recurse_proc(result, &proc) if proc
+ result
+ end
+
+ # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
+ def recurse_proc(result, &proc) # :nodoc:
+ case result
+ when Array
+ result.each { |x| recurse_proc x, &proc }
+ proc.call result
+ when Hash
+ result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
+ proc.call result
+ else
+ proc.call result
+ end
+ end
+
+ alias restore load
+ module_function :restore
+
+ class << self
+ # Sets or returns the default options for the JSON.dump method.
+ # Initially:
+ # opts = JSON.dump_default_options
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
+ attr_accessor :dump_default_options
+ end
+ self.dump_default_options = {
+ :max_nesting => false,
+ :allow_nan => true,
+ :escape_slash => false,
+ }
+
+ # :call-seq:
+ # JSON.dump(obj, io = nil, limit = nil)
+ #
+ # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
+ #
+ # The default options can be changed via method JSON.dump_default_options.
+ #
+ # - Argument +io+, if given, should respond to method +write+;
+ # the \JSON \String is written to +io+, and +io+ is returned.
+ # If +io+ is not given, the \JSON \String is returned.
+ # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
+ #
+ # ---
+ #
+ # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
+ # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
+ # json = JSON.dump(obj)
+ # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
+ #
+ # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
+ # path = 't.json'
+ # File.open(path, 'w') do |file|
+ # JSON.dump(obj, file)
+ # end # => #<File:t.json (closed)>
+ # puts File.read(path)
+ # Output:
+ # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
+ def dump(obj, anIO = nil, limit = nil)
+ if anIO and limit.nil?
+ anIO = anIO.to_io if anIO.respond_to?(:to_io)
+ unless anIO.respond_to?(:write)
+ limit = anIO
+ anIO = nil
+ end
+ end
+ opts = JSON.dump_default_options
+ opts = opts.merge(:max_nesting => limit) if limit
+ result = generate(obj, opts)
+ if anIO
+ anIO.write result
+ anIO
+ else
+ result
+ end
+ rescue JSON::NestingError
+ raise ArgumentError, "exceed depth limit"
+ end
+
+ # Encodes string using String.encode.
+ def self.iconv(to, from, string)
+ string.encode(to, from)
+ end
+end
+
+module ::Kernel
+ private
+
+ # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
+ # one line.
+ def j(*objs)
+ objs.each do |obj|
+ puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
+ end
+ nil
+ end
+
+ # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
+ # indentation and over many lines.
+ def jj(*objs)
+ objs.each do |obj|
+ puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
+ end
+ nil
+ end
+
+ # If _object_ is string-like, parse the string and return the parsed result as
+ # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
+ # structure object and return it.
+ #
+ # The _opts_ argument is passed through to generate/parse respectively. See
+ # generate and parse for their documentation.
+ def JSON(object, *args)
+ if object.respond_to? :to_str
+ JSON.parse(object.to_str, args.first)
+ else
+ JSON.generate(object, args.first)
+ end
+ end
+end
+
+# Extends any Class to include _json_creatable?_ method.
+class ::Class
+ # Returns true if this class can be used to create an instance
+ # from a serialised JSON string. The class has to implement a class
+ # method _json_create_ that expects a hash as first parameter. The hash
+ # should include the required data.
+ def json_creatable?
+ respond_to?(:json_create)
+ end
+end
diff --git a/ext/json/lib/json/ext.rb b/ext/json/lib/json/ext.rb
new file mode 100644
index 0000000000..7264a857fa
--- /dev/null
+++ b/ext/json/lib/json/ext.rb
@@ -0,0 +1,15 @@
+require 'json/common'
+
+module JSON
+ # This module holds all the modules/classes that implement JSON's
+ # functionality as C extensions.
+ module Ext
+ require 'json/ext/parser'
+ require 'json/ext/generator'
+ $DEBUG and warn "Using Ext extension for JSON."
+ JSON.parser = Parser
+ JSON.generator = Generator
+ end
+
+ JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
+end
diff --git a/ext/json/lib/json/generic_object.rb b/ext/json/lib/json/generic_object.rb
new file mode 100644
index 0000000000..108309db26
--- /dev/null
+++ b/ext/json/lib/json/generic_object.rb
@@ -0,0 +1,71 @@
+#frozen_string_literal: false
+require 'ostruct'
+
+module JSON
+ class GenericObject < OpenStruct
+ class << self
+ alias [] new
+
+ def json_creatable?
+ @json_creatable
+ end
+
+ attr_writer :json_creatable
+
+ def json_create(data)
+ data = data.dup
+ data.delete JSON.create_id
+ self[data]
+ end
+
+ def from_hash(object)
+ case
+ when object.respond_to?(:to_hash)
+ result = new
+ object.to_hash.each do |key, value|
+ result[key] = from_hash(value)
+ end
+ result
+ when object.respond_to?(:to_ary)
+ object.to_ary.map { |a| from_hash(a) }
+ else
+ object
+ end
+ end
+
+ def load(source, proc = nil, opts = {})
+ result = ::JSON.load(source, proc, opts.merge(:object_class => self))
+ result.nil? ? new : result
+ end
+
+ def dump(obj, *args)
+ ::JSON.dump(obj, *args)
+ end
+ end
+ self.json_creatable = false
+
+ def to_hash
+ table
+ end
+
+ def [](name)
+ __send__(name)
+ end unless method_defined?(:[])
+
+ def []=(name, value)
+ __send__("#{name}=", value)
+ end unless method_defined?(:[]=)
+
+ def |(other)
+ self.class[other.to_hash.merge(to_hash)]
+ end
+
+ def as_json(*)
+ { JSON.create_id => self.class.name }.merge to_hash
+ end
+
+ def to_json(*a)
+ as_json.to_json(*a)
+ end
+ end
+end
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
new file mode 100644
index 0000000000..35e8dd3252
--- /dev/null
+++ b/ext/json/lib/json/version.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: false
+module JSON
+ # JSON version
+ VERSION = '2.6.1'
+ VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
+ VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
+ VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
+ VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
+end
diff --git a/ext/json/parser/depend b/ext/json/parser/depend
new file mode 100644
index 0000000000..df261fdfbb
--- /dev/null
+++ b/ext/json/parser/depend
@@ -0,0 +1,179 @@
+$(OBJS): $(ruby_headers)
+parser.o: parser.c parser.h $(srcdir)/../fbuffer/fbuffer.h
+
+# AUTOGENERATED DEPENDENCIES START
+parser.o: $(RUBY_EXTCONF_H)
+parser.o: $(arch_hdrdir)/ruby/config.h
+parser.o: $(hdrdir)/ruby.h
+parser.o: $(hdrdir)/ruby/assert.h
+parser.o: $(hdrdir)/ruby/backward.h
+parser.o: $(hdrdir)/ruby/backward/2/assume.h
+parser.o: $(hdrdir)/ruby/backward/2/attributes.h
+parser.o: $(hdrdir)/ruby/backward/2/bool.h
+parser.o: $(hdrdir)/ruby/backward/2/inttypes.h
+parser.o: $(hdrdir)/ruby/backward/2/limits.h
+parser.o: $(hdrdir)/ruby/backward/2/long_long.h
+parser.o: $(hdrdir)/ruby/backward/2/stdalign.h
+parser.o: $(hdrdir)/ruby/backward/2/stdarg.h
+parser.o: $(hdrdir)/ruby/defines.h
+parser.o: $(hdrdir)/ruby/encoding.h
+parser.o: $(hdrdir)/ruby/intern.h
+parser.o: $(hdrdir)/ruby/internal/anyargs.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+parser.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+parser.o: $(hdrdir)/ruby/internal/assume.h
+parser.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+parser.o: $(hdrdir)/ruby/internal/attr/artificial.h
+parser.o: $(hdrdir)/ruby/internal/attr/cold.h
+parser.o: $(hdrdir)/ruby/internal/attr/const.h
+parser.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+parser.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+parser.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+parser.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+parser.o: $(hdrdir)/ruby/internal/attr/error.h
+parser.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+parser.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+parser.o: $(hdrdir)/ruby/internal/attr/format.h
+parser.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+parser.o: $(hdrdir)/ruby/internal/attr/noalias.h
+parser.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+parser.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+parser.o: $(hdrdir)/ruby/internal/attr/noinline.h
+parser.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+parser.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+parser.o: $(hdrdir)/ruby/internal/attr/pure.h
+parser.o: $(hdrdir)/ruby/internal/attr/restrict.h
+parser.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+parser.o: $(hdrdir)/ruby/internal/attr/warning.h
+parser.o: $(hdrdir)/ruby/internal/attr/weakref.h
+parser.o: $(hdrdir)/ruby/internal/cast.h
+parser.o: $(hdrdir)/ruby/internal/compiler_is.h
+parser.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+parser.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+parser.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+parser.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+parser.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+parser.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+parser.o: $(hdrdir)/ruby/internal/compiler_since.h
+parser.o: $(hdrdir)/ruby/internal/config.h
+parser.o: $(hdrdir)/ruby/internal/constant_p.h
+parser.o: $(hdrdir)/ruby/internal/core.h
+parser.o: $(hdrdir)/ruby/internal/core/rarray.h
+parser.o: $(hdrdir)/ruby/internal/core/rbasic.h
+parser.o: $(hdrdir)/ruby/internal/core/rbignum.h
+parser.o: $(hdrdir)/ruby/internal/core/rclass.h
+parser.o: $(hdrdir)/ruby/internal/core/rdata.h
+parser.o: $(hdrdir)/ruby/internal/core/rfile.h
+parser.o: $(hdrdir)/ruby/internal/core/rhash.h
+parser.o: $(hdrdir)/ruby/internal/core/robject.h
+parser.o: $(hdrdir)/ruby/internal/core/rregexp.h
+parser.o: $(hdrdir)/ruby/internal/core/rstring.h
+parser.o: $(hdrdir)/ruby/internal/core/rstruct.h
+parser.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+parser.o: $(hdrdir)/ruby/internal/ctype.h
+parser.o: $(hdrdir)/ruby/internal/dllexport.h
+parser.o: $(hdrdir)/ruby/internal/dosish.h
+parser.o: $(hdrdir)/ruby/internal/encoding/coderange.h
+parser.o: $(hdrdir)/ruby/internal/encoding/ctype.h
+parser.o: $(hdrdir)/ruby/internal/encoding/encoding.h
+parser.o: $(hdrdir)/ruby/internal/encoding/pathname.h
+parser.o: $(hdrdir)/ruby/internal/encoding/re.h
+parser.o: $(hdrdir)/ruby/internal/encoding/sprintf.h
+parser.o: $(hdrdir)/ruby/internal/encoding/string.h
+parser.o: $(hdrdir)/ruby/internal/encoding/symbol.h
+parser.o: $(hdrdir)/ruby/internal/encoding/transcode.h
+parser.o: $(hdrdir)/ruby/internal/error.h
+parser.o: $(hdrdir)/ruby/internal/eval.h
+parser.o: $(hdrdir)/ruby/internal/event.h
+parser.o: $(hdrdir)/ruby/internal/fl_type.h
+parser.o: $(hdrdir)/ruby/internal/gc.h
+parser.o: $(hdrdir)/ruby/internal/glob.h
+parser.o: $(hdrdir)/ruby/internal/globals.h
+parser.o: $(hdrdir)/ruby/internal/has/attribute.h
+parser.o: $(hdrdir)/ruby/internal/has/builtin.h
+parser.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+parser.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+parser.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+parser.o: $(hdrdir)/ruby/internal/has/extension.h
+parser.o: $(hdrdir)/ruby/internal/has/feature.h
+parser.o: $(hdrdir)/ruby/internal/has/warning.h
+parser.o: $(hdrdir)/ruby/internal/intern/array.h
+parser.o: $(hdrdir)/ruby/internal/intern/bignum.h
+parser.o: $(hdrdir)/ruby/internal/intern/class.h
+parser.o: $(hdrdir)/ruby/internal/intern/compar.h
+parser.o: $(hdrdir)/ruby/internal/intern/complex.h
+parser.o: $(hdrdir)/ruby/internal/intern/cont.h
+parser.o: $(hdrdir)/ruby/internal/intern/dir.h
+parser.o: $(hdrdir)/ruby/internal/intern/enum.h
+parser.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+parser.o: $(hdrdir)/ruby/internal/intern/error.h
+parser.o: $(hdrdir)/ruby/internal/intern/eval.h
+parser.o: $(hdrdir)/ruby/internal/intern/file.h
+parser.o: $(hdrdir)/ruby/internal/intern/gc.h
+parser.o: $(hdrdir)/ruby/internal/intern/hash.h
+parser.o: $(hdrdir)/ruby/internal/intern/io.h
+parser.o: $(hdrdir)/ruby/internal/intern/load.h
+parser.o: $(hdrdir)/ruby/internal/intern/marshal.h
+parser.o: $(hdrdir)/ruby/internal/intern/numeric.h
+parser.o: $(hdrdir)/ruby/internal/intern/object.h
+parser.o: $(hdrdir)/ruby/internal/intern/parse.h
+parser.o: $(hdrdir)/ruby/internal/intern/proc.h
+parser.o: $(hdrdir)/ruby/internal/intern/process.h
+parser.o: $(hdrdir)/ruby/internal/intern/random.h
+parser.o: $(hdrdir)/ruby/internal/intern/range.h
+parser.o: $(hdrdir)/ruby/internal/intern/rational.h
+parser.o: $(hdrdir)/ruby/internal/intern/re.h
+parser.o: $(hdrdir)/ruby/internal/intern/ruby.h
+parser.o: $(hdrdir)/ruby/internal/intern/select.h
+parser.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+parser.o: $(hdrdir)/ruby/internal/intern/signal.h
+parser.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+parser.o: $(hdrdir)/ruby/internal/intern/string.h
+parser.o: $(hdrdir)/ruby/internal/intern/struct.h
+parser.o: $(hdrdir)/ruby/internal/intern/thread.h
+parser.o: $(hdrdir)/ruby/internal/intern/time.h
+parser.o: $(hdrdir)/ruby/internal/intern/variable.h
+parser.o: $(hdrdir)/ruby/internal/intern/vm.h
+parser.o: $(hdrdir)/ruby/internal/interpreter.h
+parser.o: $(hdrdir)/ruby/internal/iterator.h
+parser.o: $(hdrdir)/ruby/internal/memory.h
+parser.o: $(hdrdir)/ruby/internal/method.h
+parser.o: $(hdrdir)/ruby/internal/module.h
+parser.o: $(hdrdir)/ruby/internal/newobj.h
+parser.o: $(hdrdir)/ruby/internal/rgengc.h
+parser.o: $(hdrdir)/ruby/internal/scan_args.h
+parser.o: $(hdrdir)/ruby/internal/special_consts.h
+parser.o: $(hdrdir)/ruby/internal/static_assert.h
+parser.o: $(hdrdir)/ruby/internal/stdalign.h
+parser.o: $(hdrdir)/ruby/internal/stdbool.h
+parser.o: $(hdrdir)/ruby/internal/symbol.h
+parser.o: $(hdrdir)/ruby/internal/value.h
+parser.o: $(hdrdir)/ruby/internal/value_type.h
+parser.o: $(hdrdir)/ruby/internal/variable.h
+parser.o: $(hdrdir)/ruby/internal/warning_push.h
+parser.o: $(hdrdir)/ruby/internal/xmalloc.h
+parser.o: $(hdrdir)/ruby/missing.h
+parser.o: $(hdrdir)/ruby/onigmo.h
+parser.o: $(hdrdir)/ruby/oniguruma.h
+parser.o: $(hdrdir)/ruby/ruby.h
+parser.o: $(hdrdir)/ruby/st.h
+parser.o: $(hdrdir)/ruby/subst.h
+parser.o: $(srcdir)/../fbuffer/fbuffer.h
+parser.o: parser.c
+parser.o: parser.h
+parser.o: parser.rl
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb
new file mode 100644
index 0000000000..feb586e1b4
--- /dev/null
+++ b/ext/json/parser/extconf.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: false
+require 'mkmf'
+
+have_func("rb_enc_raise", "ruby.h")
+have_func("rb_enc_interned_str", "ruby.h")
+
+# checking if String#-@ (str_uminus) dedupes... '
+begin
+ a = -(%w(t e s t).join)
+ b = -(%w(t e s t).join)
+ if a.equal?(b)
+ $CFLAGS << ' -DSTR_UMINUS_DEDUPE=1 '
+ else
+ $CFLAGS << ' -DSTR_UMINUS_DEDUPE=0 '
+ end
+rescue NoMethodError
+ $CFLAGS << ' -DSTR_UMINUS_DEDUPE=0 '
+end
+
+# checking if String#-@ (str_uminus) directly interns frozen strings... '
+begin
+ s = rand.to_s.freeze
+ if (-s).equal?(s) && (-s.dup).equal?(s)
+ $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=1 '
+ else
+ $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=0 '
+ end
+rescue NoMethodError
+ $CFLAGS << ' -DSTR_UMINUS_DEDUPE_FROZEN=0 '
+end
+
+create_makefile 'json/ext/parser'
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
new file mode 100644
index 0000000000..b1dc8810c3
--- /dev/null
+++ b/ext/json/parser/parser.c
@@ -0,0 +1,3338 @@
+/* This file is automatically generated from parser.rl by using ragel */
+#line 1 "parser.rl"
+#include "../fbuffer/fbuffer.h"
+#include "parser.h"
+
+#if defined HAVE_RUBY_ENCODING_H
+# define EXC_ENCODING rb_utf8_encoding(),
+# ifndef HAVE_RB_ENC_RAISE
+static void
+enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
+{
+ va_list args;
+ VALUE mesg;
+
+ va_start(args, fmt);
+ mesg = rb_enc_vsprintf(enc, fmt, args);
+ va_end(args);
+
+ rb_exc_raise(rb_exc_new3(exc, mesg));
+}
+# define rb_enc_raise enc_raise
+# endif
+#else
+# define EXC_ENCODING /* nothing */
+# define rb_enc_raise rb_raise
+#endif
+
+/* unicode */
+
+static const signed char digit_values[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+};
+
+static UTF32 unescape_unicode(const unsigned char *p)
+{
+ signed char b;
+ UTF32 result = 0;
+ b = digit_values[p[0]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[1]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[2]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[3]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ return result;
+}
+
+static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
+{
+ int len = 1;
+ if (ch <= 0x7F) {
+ buf[0] = (char) ch;
+ } else if (ch <= 0x07FF) {
+ buf[0] = (char) ((ch >> 6) | 0xC0);
+ buf[1] = (char) ((ch & 0x3F) | 0x80);
+ len++;
+ } else if (ch <= 0xFFFF) {
+ buf[0] = (char) ((ch >> 12) | 0xE0);
+ buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[2] = (char) ((ch & 0x3F) | 0x80);
+ len += 2;
+ } else if (ch <= 0x1fffff) {
+ buf[0] =(char) ((ch >> 18) | 0xF0);
+ buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
+ buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[3] =(char) ((ch & 0x3F) | 0x80);
+ len += 3;
+ } else {
+ buf[0] = '?';
+ }
+ return len;
+}
+
+static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
+static VALUE CNaN, CInfinity, CMinusInfinity;
+
+static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
+i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
+i_object_class, i_array_class, i_decimal_class, i_key_p,
+i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
+i_leftshift, i_new, i_try_convert, i_freeze, i_uminus;
+
+
+#line 125 "parser.rl"
+
+
+
+enum {JSON_object_start = 1};
+enum {JSON_object_first_final = 27};
+enum {JSON_object_error = 0};
+
+enum {JSON_object_en_main = 1};
+
+static const char MAYBE_UNUSED(_JSON_object_nfa_targs)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_object_nfa_offsets)[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_object_nfa_push_actions)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_object_nfa_pop_trans)[] = {
+ 0, 0
+};
+
+
+#line 167 "parser.rl"
+
+
+static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
+{
+ int cs = EVIL;
+ VALUE last_name = Qnil;
+ VALUE object_class = json->object_class;
+
+ if (json->max_nesting && current_nesting > json->max_nesting) {
+ rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
+ }
+
+ *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
+
+
+ {
+ cs = (int)JSON_object_start;
+ }
+
+ #line 182 "parser.rl"
+
+
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+ case 1:
+ goto st_case_1;
+ case 0:
+ goto st_case_0;
+ case 2:
+ goto st_case_2;
+ case 3:
+ goto st_case_3;
+ case 4:
+ goto st_case_4;
+ case 5:
+ goto st_case_5;
+ case 6:
+ goto st_case_6;
+ case 7:
+ goto st_case_7;
+ case 8:
+ goto st_case_8;
+ case 9:
+ goto st_case_9;
+ case 10:
+ goto st_case_10;
+ case 11:
+ goto st_case_11;
+ case 12:
+ goto st_case_12;
+ case 13:
+ goto st_case_13;
+ case 14:
+ goto st_case_14;
+ case 15:
+ goto st_case_15;
+ case 16:
+ goto st_case_16;
+ case 17:
+ goto st_case_17;
+ case 18:
+ goto st_case_18;
+ case 27:
+ goto st_case_27;
+ case 19:
+ goto st_case_19;
+ case 20:
+ goto st_case_20;
+ case 21:
+ goto st_case_21;
+ case 22:
+ goto st_case_22;
+ case 23:
+ goto st_case_23;
+ case 24:
+ goto st_case_24;
+ case 25:
+ goto st_case_25;
+ case 26:
+ goto st_case_26;
+ }
+ goto st_out;
+ st_case_1:
+ if ( ( (*( p))) == 123 ) {
+ goto st2;
+ }
+ {
+ goto st0;
+ }
+ st_case_0:
+ st0:
+ cs = 0;
+ goto _out;
+ st2:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof2;
+ st_case_2:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st2;
+ }
+ case 32: {
+ goto st2;
+ }
+ case 34: {
+ goto ctr2;
+ }
+ case 47: {
+ goto st23;
+ }
+ case 125: {
+ goto ctr4;
+ }
+ }
+ if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
+ goto st2;
+ }
+ {
+ goto st0;
+ }
+ ctr2:
+ {
+ #line 149 "parser.rl"
+
+ char *np;
+ json->parsing_name = 1;
+ np = JSON_parse_string(json, p, pe, &last_name);
+ json->parsing_name = 0;
+ if (np == NULL) { {p = p - 1; } {p+= 1; cs = 3; goto _out;} } else {p = (( np))-1;}
+
+ }
+
+ goto st3;
+ st3:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof3;
+ st_case_3:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st3;
+ }
+ case 32: {
+ goto st3;
+ }
+ case 47: {
+ goto st4;
+ }
+ case 58: {
+ goto st8;
+ }
+ }
+ if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
+ goto st3;
+ }
+ {
+ goto st0;
+ }
+ st4:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof4;
+ st_case_4:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st5;
+ }
+ case 47: {
+ goto st7;
+ }
+ }
+ {
+ goto st0;
+ }
+ st5:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof5;
+ st_case_5:
+ if ( ( (*( p))) == 42 ) {
+ goto st6;
+ }
+ {
+ goto st5;
+ }
+ st6:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof6;
+ st_case_6:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st6;
+ }
+ case 47: {
+ goto st3;
+ }
+ }
+ {
+ goto st5;
+ }
+ st7:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof7;
+ st_case_7:
+ if ( ( (*( p))) == 10 ) {
+ goto st3;
+ }
+ {
+ goto st7;
+ }
+ st8:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof8;
+ st_case_8:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st8;
+ }
+ case 32: {
+ goto st8;
+ }
+ case 34: {
+ goto ctr11;
+ }
+ case 45: {
+ goto ctr11;
+ }
+ case 47: {
+ goto st19;
+ }
+ case 73: {
+ goto ctr11;
+ }
+ case 78: {
+ goto ctr11;
+ }
+ case 91: {
+ goto ctr11;
+ }
+ case 102: {
+ goto ctr11;
+ }
+ case 110: {
+ goto ctr11;
+ }
+ case 116: {
+ goto ctr11;
+ }
+ case 123: {
+ goto ctr11;
+ }
+ }
+ if ( ( (*( p))) > 10 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto ctr11;
+ }
+ } else if ( ( (*( p))) >= 9 ) {
+ goto st8;
+ }
+ {
+ goto st0;
+ }
+ ctr11:
+ {
+ #line 133 "parser.rl"
+
+ VALUE v = Qnil;
+ char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
+ if (np == NULL) {
+ {p = p - 1; } {p+= 1; cs = 9; goto _out;}
+ } else {
+ if (NIL_P(json->object_class)) {
+ OBJ_FREEZE(last_name);
+ rb_hash_aset(*result, last_name, v);
+ } else {
+ rb_funcall(*result, i_aset, 2, last_name, v);
+ }
+ {p = (( np))-1;}
+
+ }
+ }
+
+ goto st9;
+ st9:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof9;
+ st_case_9:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st9;
+ }
+ case 32: {
+ goto st9;
+ }
+ case 44: {
+ goto st10;
+ }
+ case 47: {
+ goto st15;
+ }
+ case 125: {
+ goto ctr4;
+ }
+ }
+ if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
+ goto st9;
+ }
+ {
+ goto st0;
+ }
+ st10:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof10;
+ st_case_10:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st10;
+ }
+ case 32: {
+ goto st10;
+ }
+ case 34: {
+ goto ctr2;
+ }
+ case 47: {
+ goto st11;
+ }
+ }
+ if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
+ goto st10;
+ }
+ {
+ goto st0;
+ }
+ st11:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof11;
+ st_case_11:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st12;
+ }
+ case 47: {
+ goto st14;
+ }
+ }
+ {
+ goto st0;
+ }
+ st12:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof12;
+ st_case_12:
+ if ( ( (*( p))) == 42 ) {
+ goto st13;
+ }
+ {
+ goto st12;
+ }
+ st13:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof13;
+ st_case_13:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st13;
+ }
+ case 47: {
+ goto st10;
+ }
+ }
+ {
+ goto st12;
+ }
+ st14:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof14;
+ st_case_14:
+ if ( ( (*( p))) == 10 ) {
+ goto st10;
+ }
+ {
+ goto st14;
+ }
+ st15:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof15;
+ st_case_15:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st16;
+ }
+ case 47: {
+ goto st18;
+ }
+ }
+ {
+ goto st0;
+ }
+ st16:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof16;
+ st_case_16:
+ if ( ( (*( p))) == 42 ) {
+ goto st17;
+ }
+ {
+ goto st16;
+ }
+ st17:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof17;
+ st_case_17:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st17;
+ }
+ case 47: {
+ goto st9;
+ }
+ }
+ {
+ goto st16;
+ }
+ st18:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof18;
+ st_case_18:
+ if ( ( (*( p))) == 10 ) {
+ goto st9;
+ }
+ {
+ goto st18;
+ }
+ ctr4:
+ {
+ #line 157 "parser.rl"
+ {p = p - 1; } {p+= 1; cs = 27; goto _out;} }
+
+ goto st27;
+ st27:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof27;
+ st_case_27:
+ {
+ goto st0;
+ }
+ st19:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof19;
+ st_case_19:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st20;
+ }
+ case 47: {
+ goto st22;
+ }
+ }
+ {
+ goto st0;
+ }
+ st20:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof20;
+ st_case_20:
+ if ( ( (*( p))) == 42 ) {
+ goto st21;
+ }
+ {
+ goto st20;
+ }
+ st21:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof21;
+ st_case_21:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st21;
+ }
+ case 47: {
+ goto st8;
+ }
+ }
+ {
+ goto st20;
+ }
+ st22:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof22;
+ st_case_22:
+ if ( ( (*( p))) == 10 ) {
+ goto st8;
+ }
+ {
+ goto st22;
+ }
+ st23:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof23;
+ st_case_23:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st24;
+ }
+ case 47: {
+ goto st26;
+ }
+ }
+ {
+ goto st0;
+ }
+ st24:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof24;
+ st_case_24:
+ if ( ( (*( p))) == 42 ) {
+ goto st25;
+ }
+ {
+ goto st24;
+ }
+ st25:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof25;
+ st_case_25:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st25;
+ }
+ case 47: {
+ goto st2;
+ }
+ }
+ {
+ goto st24;
+ }
+ st26:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof26;
+ st_case_26:
+ if ( ( (*( p))) == 10 ) {
+ goto st2;
+ }
+ {
+ goto st26;
+ }
+ st_out:
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof18: cs = 18; goto _test_eof;
+ _test_eof27: cs = 27; goto _test_eof;
+ _test_eof19: cs = 19; goto _test_eof;
+ _test_eof20: cs = 20; goto _test_eof;
+ _test_eof21: cs = 21; goto _test_eof;
+ _test_eof22: cs = 22; goto _test_eof;
+ _test_eof23: cs = 23; goto _test_eof;
+ _test_eof24: cs = 24; goto _test_eof;
+ _test_eof25: cs = 25; goto _test_eof;
+ _test_eof26: cs = 26; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+ #line 183 "parser.rl"
+
+
+ if (cs >= JSON_object_first_final) {
+ if (json->create_additions) {
+ VALUE klassname;
+ if (NIL_P(json->object_class)) {
+ klassname = rb_hash_aref(*result, json->create_id);
+ } else {
+ klassname = rb_funcall(*result, i_aref, 1, json->create_id);
+ }
+ if (!NIL_P(klassname)) {
+ VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
+ }
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+
+
+enum {JSON_value_start = 1};
+enum {JSON_value_first_final = 29};
+enum {JSON_value_error = 0};
+
+enum {JSON_value_en_main = 1};
+
+static const char MAYBE_UNUSED(_JSON_value_nfa_targs)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_value_nfa_offsets)[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_value_nfa_push_actions)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_value_nfa_pop_trans)[] = {
+ 0, 0
+};
+
+
+#line 283 "parser.rl"
+
+
+static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
+{
+ int cs = EVIL;
+
+
+ {
+ cs = (int)JSON_value_start;
+ }
+
+ #line 290 "parser.rl"
+
+
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+ case 1:
+ goto st_case_1;
+ case 0:
+ goto st_case_0;
+ case 29:
+ goto st_case_29;
+ case 2:
+ goto st_case_2;
+ case 3:
+ goto st_case_3;
+ case 4:
+ goto st_case_4;
+ case 5:
+ goto st_case_5;
+ case 6:
+ goto st_case_6;
+ case 7:
+ goto st_case_7;
+ case 8:
+ goto st_case_8;
+ case 9:
+ goto st_case_9;
+ case 10:
+ goto st_case_10;
+ case 11:
+ goto st_case_11;
+ case 12:
+ goto st_case_12;
+ case 13:
+ goto st_case_13;
+ case 14:
+ goto st_case_14;
+ case 15:
+ goto st_case_15;
+ case 16:
+ goto st_case_16;
+ case 17:
+ goto st_case_17;
+ case 18:
+ goto st_case_18;
+ case 19:
+ goto st_case_19;
+ case 20:
+ goto st_case_20;
+ case 21:
+ goto st_case_21;
+ case 22:
+ goto st_case_22;
+ case 23:
+ goto st_case_23;
+ case 24:
+ goto st_case_24;
+ case 25:
+ goto st_case_25;
+ case 26:
+ goto st_case_26;
+ case 27:
+ goto st_case_27;
+ case 28:
+ goto st_case_28;
+ }
+ goto st_out;
+ st1:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof1;
+ st_case_1:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st1;
+ }
+ case 32: {
+ goto st1;
+ }
+ case 34: {
+ goto ctr2;
+ }
+ case 45: {
+ goto ctr3;
+ }
+ case 47: {
+ goto st6;
+ }
+ case 73: {
+ goto st10;
+ }
+ case 78: {
+ goto st17;
+ }
+ case 91: {
+ goto ctr7;
+ }
+ case 102: {
+ goto st19;
+ }
+ case 110: {
+ goto st23;
+ }
+ case 116: {
+ goto st26;
+ }
+ case 123: {
+ goto ctr11;
+ }
+ }
+ if ( ( (*( p))) > 10 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto ctr3;
+ }
+ } else if ( ( (*( p))) >= 9 ) {
+ goto st1;
+ }
+ {
+ goto st0;
+ }
+ st_case_0:
+ st0:
+ cs = 0;
+ goto _out;
+ ctr2:
+ {
+ #line 235 "parser.rl"
+
+ char *np = JSON_parse_string(json, p, pe, result);
+ if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;}
+
+ }
+
+ goto st29;
+ ctr3:
+ {
+ #line 240 "parser.rl"
+
+ char *np;
+ if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
+ if (json->allow_nan) {
+ *result = CMinusInfinity;
+ {p = (( p + 10))-1;}
+
+ {p = p - 1; } {p+= 1; cs = 29; goto _out;}
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ }
+ }
+ np = JSON_parse_float(json, p, pe, result);
+ if (np != NULL) {p = (( np))-1;}
+
+ np = JSON_parse_integer(json, p, pe, result);
+ if (np != NULL) {p = (( np))-1;}
+
+ {p = p - 1; } {p+= 1; cs = 29; goto _out;}
+ }
+
+ goto st29;
+ ctr7:
+ {
+ #line 258 "parser.rl"
+
+ char *np;
+ np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
+ if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;}
+
+ }
+
+ goto st29;
+ ctr11:
+ {
+ #line 264 "parser.rl"
+
+ char *np;
+ np = JSON_parse_object(json, p, pe, result, current_nesting + 1);
+ if (np == NULL) { {p = p - 1; } {p+= 1; cs = 29; goto _out;} } else {p = (( np))-1;}
+
+ }
+
+ goto st29;
+ ctr25:
+ {
+ #line 228 "parser.rl"
+
+ if (json->allow_nan) {
+ *result = CInfinity;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
+ }
+ }
+
+ goto st29;
+ ctr27:
+ {
+ #line 221 "parser.rl"
+
+ if (json->allow_nan) {
+ *result = CNaN;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
+ }
+ }
+
+ goto st29;
+ ctr31:
+ {
+ #line 215 "parser.rl"
+
+ *result = Qfalse;
+ }
+
+ goto st29;
+ ctr34:
+ {
+ #line 212 "parser.rl"
+
+ *result = Qnil;
+ }
+
+ goto st29;
+ ctr37:
+ {
+ #line 218 "parser.rl"
+
+ *result = Qtrue;
+ }
+
+ goto st29;
+ st29:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof29;
+ st_case_29:
+ {
+ #line 270 "parser.rl"
+ {p = p - 1; } {p+= 1; cs = 29; goto _out;} }
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st29;
+ }
+ case 32: {
+ goto st29;
+ }
+ case 47: {
+ goto st2;
+ }
+ }
+ if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
+ goto st29;
+ }
+ {
+ goto st0;
+ }
+ st2:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof2;
+ st_case_2:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st3;
+ }
+ case 47: {
+ goto st5;
+ }
+ }
+ {
+ goto st0;
+ }
+ st3:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof3;
+ st_case_3:
+ if ( ( (*( p))) == 42 ) {
+ goto st4;
+ }
+ {
+ goto st3;
+ }
+ st4:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof4;
+ st_case_4:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st4;
+ }
+ case 47: {
+ goto st29;
+ }
+ }
+ {
+ goto st3;
+ }
+ st5:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof5;
+ st_case_5:
+ if ( ( (*( p))) == 10 ) {
+ goto st29;
+ }
+ {
+ goto st5;
+ }
+ st6:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof6;
+ st_case_6:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st7;
+ }
+ case 47: {
+ goto st9;
+ }
+ }
+ {
+ goto st0;
+ }
+ st7:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof7;
+ st_case_7:
+ if ( ( (*( p))) == 42 ) {
+ goto st8;
+ }
+ {
+ goto st7;
+ }
+ st8:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof8;
+ st_case_8:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st8;
+ }
+ case 47: {
+ goto st1;
+ }
+ }
+ {
+ goto st7;
+ }
+ st9:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof9;
+ st_case_9:
+ if ( ( (*( p))) == 10 ) {
+ goto st1;
+ }
+ {
+ goto st9;
+ }
+ st10:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof10;
+ st_case_10:
+ if ( ( (*( p))) == 110 ) {
+ goto st11;
+ }
+ {
+ goto st0;
+ }
+ st11:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof11;
+ st_case_11:
+ if ( ( (*( p))) == 102 ) {
+ goto st12;
+ }
+ {
+ goto st0;
+ }
+ st12:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof12;
+ st_case_12:
+ if ( ( (*( p))) == 105 ) {
+ goto st13;
+ }
+ {
+ goto st0;
+ }
+ st13:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof13;
+ st_case_13:
+ if ( ( (*( p))) == 110 ) {
+ goto st14;
+ }
+ {
+ goto st0;
+ }
+ st14:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof14;
+ st_case_14:
+ if ( ( (*( p))) == 105 ) {
+ goto st15;
+ }
+ {
+ goto st0;
+ }
+ st15:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof15;
+ st_case_15:
+ if ( ( (*( p))) == 116 ) {
+ goto st16;
+ }
+ {
+ goto st0;
+ }
+ st16:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof16;
+ st_case_16:
+ if ( ( (*( p))) == 121 ) {
+ goto ctr25;
+ }
+ {
+ goto st0;
+ }
+ st17:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof17;
+ st_case_17:
+ if ( ( (*( p))) == 97 ) {
+ goto st18;
+ }
+ {
+ goto st0;
+ }
+ st18:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof18;
+ st_case_18:
+ if ( ( (*( p))) == 78 ) {
+ goto ctr27;
+ }
+ {
+ goto st0;
+ }
+ st19:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof19;
+ st_case_19:
+ if ( ( (*( p))) == 97 ) {
+ goto st20;
+ }
+ {
+ goto st0;
+ }
+ st20:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof20;
+ st_case_20:
+ if ( ( (*( p))) == 108 ) {
+ goto st21;
+ }
+ {
+ goto st0;
+ }
+ st21:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof21;
+ st_case_21:
+ if ( ( (*( p))) == 115 ) {
+ goto st22;
+ }
+ {
+ goto st0;
+ }
+ st22:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof22;
+ st_case_22:
+ if ( ( (*( p))) == 101 ) {
+ goto ctr31;
+ }
+ {
+ goto st0;
+ }
+ st23:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof23;
+ st_case_23:
+ if ( ( (*( p))) == 117 ) {
+ goto st24;
+ }
+ {
+ goto st0;
+ }
+ st24:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof24;
+ st_case_24:
+ if ( ( (*( p))) == 108 ) {
+ goto st25;
+ }
+ {
+ goto st0;
+ }
+ st25:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof25;
+ st_case_25:
+ if ( ( (*( p))) == 108 ) {
+ goto ctr34;
+ }
+ {
+ goto st0;
+ }
+ st26:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof26;
+ st_case_26:
+ if ( ( (*( p))) == 114 ) {
+ goto st27;
+ }
+ {
+ goto st0;
+ }
+ st27:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof27;
+ st_case_27:
+ if ( ( (*( p))) == 117 ) {
+ goto st28;
+ }
+ {
+ goto st0;
+ }
+ st28:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof28;
+ st_case_28:
+ if ( ( (*( p))) == 101 ) {
+ goto ctr37;
+ }
+ {
+ goto st0;
+ }
+ st_out:
+ _test_eof1: cs = 1; goto _test_eof;
+ _test_eof29: cs = 29; goto _test_eof;
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof18: cs = 18; goto _test_eof;
+ _test_eof19: cs = 19; goto _test_eof;
+ _test_eof20: cs = 20; goto _test_eof;
+ _test_eof21: cs = 21; goto _test_eof;
+ _test_eof22: cs = 22; goto _test_eof;
+ _test_eof23: cs = 23; goto _test_eof;
+ _test_eof24: cs = 24; goto _test_eof;
+ _test_eof25: cs = 25; goto _test_eof;
+ _test_eof26: cs = 26; goto _test_eof;
+ _test_eof27: cs = 27; goto _test_eof;
+ _test_eof28: cs = 28; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+ #line 291 "parser.rl"
+
+
+ if (json->freeze) {
+ OBJ_FREEZE(*result);
+ }
+
+ if (cs >= JSON_value_first_final) {
+ return p;
+ } else {
+ return NULL;
+ }
+}
+
+
+enum {JSON_integer_start = 1};
+enum {JSON_integer_first_final = 3};
+enum {JSON_integer_error = 0};
+
+enum {JSON_integer_en_main = 1};
+
+static const char MAYBE_UNUSED(_JSON_integer_nfa_targs)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_integer_nfa_offsets)[] = {
+ 0, 0, 0, 0, 0, 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_integer_nfa_push_actions)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_integer_nfa_pop_trans)[] = {
+ 0, 0
+};
+
+
+#line 311 "parser.rl"
+
+
+static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
+{
+ int cs = EVIL;
+
+
+ {
+ cs = (int)JSON_integer_start;
+ }
+
+ #line 318 "parser.rl"
+
+ json->memo = p;
+
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+ case 1:
+ goto st_case_1;
+ case 0:
+ goto st_case_0;
+ case 2:
+ goto st_case_2;
+ case 3:
+ goto st_case_3;
+ case 4:
+ goto st_case_4;
+ case 5:
+ goto st_case_5;
+ }
+ goto st_out;
+ st_case_1:
+ switch( ( (*( p))) ) {
+ case 45: {
+ goto st2;
+ }
+ case 48: {
+ goto st3;
+ }
+ }
+ if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st5;
+ }
+ {
+ goto st0;
+ }
+ st_case_0:
+ st0:
+ cs = 0;
+ goto _out;
+ st2:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof2;
+ st_case_2:
+ if ( ( (*( p))) == 48 ) {
+ goto st3;
+ }
+ if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st5;
+ }
+ {
+ goto st0;
+ }
+ st3:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof3;
+ st_case_3:
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st0;
+ }
+ {
+ goto ctr4;
+ }
+ ctr4:
+ {
+ #line 308 "parser.rl"
+ {p = p - 1; } {p+= 1; cs = 4; goto _out;} }
+
+ goto st4;
+ st4:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof4;
+ st_case_4:
+ {
+ goto st0;
+ }
+ st5:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof5;
+ st_case_5:
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st5;
+ }
+ {
+ goto ctr4;
+ }
+ st_out:
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+ #line 320 "parser.rl"
+
+
+ if (cs >= JSON_integer_first_final) {
+ long len = p - json->memo;
+ fbuffer_clear(json->fbuffer);
+ fbuffer_append(json->fbuffer, json->memo, len);
+ fbuffer_append_char(json->fbuffer, '\0');
+ *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10);
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+
+enum {JSON_float_start = 1};
+enum {JSON_float_first_final = 8};
+enum {JSON_float_error = 0};
+
+enum {JSON_float_en_main = 1};
+
+static const char MAYBE_UNUSED(_JSON_float_nfa_targs)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_float_nfa_offsets)[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_float_nfa_push_actions)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_float_nfa_pop_trans)[] = {
+ 0, 0
+};
+
+
+#line 345 "parser.rl"
+
+
+static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
+{
+ int cs = EVIL;
+
+
+ {
+ cs = (int)JSON_float_start;
+ }
+
+ #line 352 "parser.rl"
+
+ json->memo = p;
+
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+ case 1:
+ goto st_case_1;
+ case 0:
+ goto st_case_0;
+ case 2:
+ goto st_case_2;
+ case 3:
+ goto st_case_3;
+ case 4:
+ goto st_case_4;
+ case 8:
+ goto st_case_8;
+ case 9:
+ goto st_case_9;
+ case 5:
+ goto st_case_5;
+ case 6:
+ goto st_case_6;
+ case 10:
+ goto st_case_10;
+ case 7:
+ goto st_case_7;
+ }
+ goto st_out;
+ st_case_1:
+ switch( ( (*( p))) ) {
+ case 45: {
+ goto st2;
+ }
+ case 48: {
+ goto st3;
+ }
+ }
+ if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st7;
+ }
+ {
+ goto st0;
+ }
+ st_case_0:
+ st0:
+ cs = 0;
+ goto _out;
+ st2:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof2;
+ st_case_2:
+ if ( ( (*( p))) == 48 ) {
+ goto st3;
+ }
+ if ( 49 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st7;
+ }
+ {
+ goto st0;
+ }
+ st3:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof3;
+ st_case_3:
+ switch( ( (*( p))) ) {
+ case 46: {
+ goto st4;
+ }
+ case 69: {
+ goto st5;
+ }
+ case 101: {
+ goto st5;
+ }
+ }
+ {
+ goto st0;
+ }
+ st4:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof4;
+ st_case_4:
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st8;
+ }
+ {
+ goto st0;
+ }
+ st8:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof8;
+ st_case_8:
+ switch( ( (*( p))) ) {
+ case 69: {
+ goto st5;
+ }
+ case 101: {
+ goto st5;
+ }
+ }
+ if ( ( (*( p))) > 46 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st8;
+ }
+ } else if ( ( (*( p))) >= 45 ) {
+ goto st0;
+ }
+ {
+ goto ctr9;
+ }
+ ctr9:
+ {
+ #line 339 "parser.rl"
+ {p = p - 1; } {p+= 1; cs = 9; goto _out;} }
+
+ goto st9;
+ st9:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof9;
+ st_case_9:
+ {
+ goto st0;
+ }
+ st5:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof5;
+ st_case_5:
+ switch( ( (*( p))) ) {
+ case 43: {
+ goto st6;
+ }
+ case 45: {
+ goto st6;
+ }
+ }
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st10;
+ }
+ {
+ goto st0;
+ }
+ st6:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof6;
+ st_case_6:
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st10;
+ }
+ {
+ goto st0;
+ }
+ st10:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof10;
+ st_case_10:
+ switch( ( (*( p))) ) {
+ case 69: {
+ goto st0;
+ }
+ case 101: {
+ goto st0;
+ }
+ }
+ if ( ( (*( p))) > 46 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st10;
+ }
+ } else if ( ( (*( p))) >= 45 ) {
+ goto st0;
+ }
+ {
+ goto ctr9;
+ }
+ st7:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof7;
+ st_case_7:
+ switch( ( (*( p))) ) {
+ case 46: {
+ goto st4;
+ }
+ case 69: {
+ goto st5;
+ }
+ case 101: {
+ goto st5;
+ }
+ }
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st7;
+ }
+ {
+ goto st0;
+ }
+ st_out:
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+ #line 354 "parser.rl"
+
+
+ if (cs >= JSON_float_first_final) {
+ VALUE mod = Qnil;
+ ID method_id = 0;
+ if (rb_respond_to(json->decimal_class, i_try_convert)) {
+ mod = json->decimal_class;
+ method_id = i_try_convert;
+ } else if (rb_respond_to(json->decimal_class, i_new)) {
+ mod = json->decimal_class;
+ method_id = i_new;
+ } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) {
+ VALUE name = rb_class_name(json->decimal_class);
+ const char *name_cstr = RSTRING_PTR(name);
+ const char *last_colon = strrchr(name_cstr, ':');
+ if (last_colon) {
+ const char *mod_path_end = last_colon - 1;
+ VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
+ mod = rb_path_to_class(mod_path);
+
+ const char *method_name_beg = last_colon + 1;
+ long before_len = method_name_beg - name_cstr;
+ long len = RSTRING_LEN(name) - before_len;
+ VALUE method_name = rb_str_substr(name, before_len, len);
+ method_id = SYM2ID(rb_str_intern(method_name));
+ } else {
+ mod = rb_mKernel;
+ method_id = SYM2ID(rb_str_intern(name));
+ }
+ }
+
+ long len = p - json->memo;
+ fbuffer_clear(json->fbuffer);
+ fbuffer_append(json->fbuffer, json->memo, len);
+ fbuffer_append_char(json->fbuffer, '\0');
+
+ if (method_id) {
+ VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+ *result = rb_funcallv(mod, method_id, 1, &text);
+ } else {
+ *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+ }
+
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+
+
+enum {JSON_array_start = 1};
+enum {JSON_array_first_final = 17};
+enum {JSON_array_error = 0};
+
+enum {JSON_array_en_main = 1};
+
+static const char MAYBE_UNUSED(_JSON_array_nfa_targs)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_array_nfa_offsets)[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_array_nfa_push_actions)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_array_nfa_pop_trans)[] = {
+ 0, 0
+};
+
+
+#line 432 "parser.rl"
+
+
+static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
+{
+ int cs = EVIL;
+ VALUE array_class = json->array_class;
+
+ if (json->max_nesting && current_nesting > json->max_nesting) {
+ rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
+ }
+ *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
+
+
+ {
+ cs = (int)JSON_array_start;
+ }
+
+ #line 445 "parser.rl"
+
+
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+ case 1:
+ goto st_case_1;
+ case 0:
+ goto st_case_0;
+ case 2:
+ goto st_case_2;
+ case 3:
+ goto st_case_3;
+ case 4:
+ goto st_case_4;
+ case 5:
+ goto st_case_5;
+ case 6:
+ goto st_case_6;
+ case 7:
+ goto st_case_7;
+ case 8:
+ goto st_case_8;
+ case 9:
+ goto st_case_9;
+ case 10:
+ goto st_case_10;
+ case 11:
+ goto st_case_11;
+ case 12:
+ goto st_case_12;
+ case 17:
+ goto st_case_17;
+ case 13:
+ goto st_case_13;
+ case 14:
+ goto st_case_14;
+ case 15:
+ goto st_case_15;
+ case 16:
+ goto st_case_16;
+ }
+ goto st_out;
+ st_case_1:
+ if ( ( (*( p))) == 91 ) {
+ goto st2;
+ }
+ {
+ goto st0;
+ }
+ st_case_0:
+ st0:
+ cs = 0;
+ goto _out;
+ st2:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof2;
+ st_case_2:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st2;
+ }
+ case 32: {
+ goto st2;
+ }
+ case 34: {
+ goto ctr2;
+ }
+ case 45: {
+ goto ctr2;
+ }
+ case 47: {
+ goto st13;
+ }
+ case 73: {
+ goto ctr2;
+ }
+ case 78: {
+ goto ctr2;
+ }
+ case 91: {
+ goto ctr2;
+ }
+ case 93: {
+ goto ctr4;
+ }
+ case 102: {
+ goto ctr2;
+ }
+ case 110: {
+ goto ctr2;
+ }
+ case 116: {
+ goto ctr2;
+ }
+ case 123: {
+ goto ctr2;
+ }
+ }
+ if ( ( (*( p))) > 10 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto ctr2;
+ }
+ } else if ( ( (*( p))) >= 9 ) {
+ goto st2;
+ }
+ {
+ goto st0;
+ }
+ ctr2:
+ {
+ #line 409 "parser.rl"
+
+ VALUE v = Qnil;
+ char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
+ if (np == NULL) {
+ {p = p - 1; } {p+= 1; cs = 3; goto _out;}
+ } else {
+ if (NIL_P(json->array_class)) {
+ rb_ary_push(*result, v);
+ } else {
+ rb_funcall(*result, i_leftshift, 1, v);
+ }
+ {p = (( np))-1;}
+
+ }
+ }
+
+ goto st3;
+ st3:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof3;
+ st_case_3:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st3;
+ }
+ case 32: {
+ goto st3;
+ }
+ case 44: {
+ goto st4;
+ }
+ case 47: {
+ goto st9;
+ }
+ case 93: {
+ goto ctr4;
+ }
+ }
+ if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
+ goto st3;
+ }
+ {
+ goto st0;
+ }
+ st4:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof4;
+ st_case_4:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st4;
+ }
+ case 32: {
+ goto st4;
+ }
+ case 34: {
+ goto ctr2;
+ }
+ case 45: {
+ goto ctr2;
+ }
+ case 47: {
+ goto st5;
+ }
+ case 73: {
+ goto ctr2;
+ }
+ case 78: {
+ goto ctr2;
+ }
+ case 91: {
+ goto ctr2;
+ }
+ case 102: {
+ goto ctr2;
+ }
+ case 110: {
+ goto ctr2;
+ }
+ case 116: {
+ goto ctr2;
+ }
+ case 123: {
+ goto ctr2;
+ }
+ }
+ if ( ( (*( p))) > 10 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto ctr2;
+ }
+ } else if ( ( (*( p))) >= 9 ) {
+ goto st4;
+ }
+ {
+ goto st0;
+ }
+ st5:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof5;
+ st_case_5:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st6;
+ }
+ case 47: {
+ goto st8;
+ }
+ }
+ {
+ goto st0;
+ }
+ st6:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof6;
+ st_case_6:
+ if ( ( (*( p))) == 42 ) {
+ goto st7;
+ }
+ {
+ goto st6;
+ }
+ st7:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof7;
+ st_case_7:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st7;
+ }
+ case 47: {
+ goto st4;
+ }
+ }
+ {
+ goto st6;
+ }
+ st8:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof8;
+ st_case_8:
+ if ( ( (*( p))) == 10 ) {
+ goto st4;
+ }
+ {
+ goto st8;
+ }
+ st9:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof9;
+ st_case_9:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st10;
+ }
+ case 47: {
+ goto st12;
+ }
+ }
+ {
+ goto st0;
+ }
+ st10:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof10;
+ st_case_10:
+ if ( ( (*( p))) == 42 ) {
+ goto st11;
+ }
+ {
+ goto st10;
+ }
+ st11:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof11;
+ st_case_11:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st11;
+ }
+ case 47: {
+ goto st3;
+ }
+ }
+ {
+ goto st10;
+ }
+ st12:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof12;
+ st_case_12:
+ if ( ( (*( p))) == 10 ) {
+ goto st3;
+ }
+ {
+ goto st12;
+ }
+ ctr4:
+ {
+ #line 424 "parser.rl"
+ {p = p - 1; } {p+= 1; cs = 17; goto _out;} }
+
+ goto st17;
+ st17:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof17;
+ st_case_17:
+ {
+ goto st0;
+ }
+ st13:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof13;
+ st_case_13:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st14;
+ }
+ case 47: {
+ goto st16;
+ }
+ }
+ {
+ goto st0;
+ }
+ st14:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof14;
+ st_case_14:
+ if ( ( (*( p))) == 42 ) {
+ goto st15;
+ }
+ {
+ goto st14;
+ }
+ st15:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof15;
+ st_case_15:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st15;
+ }
+ case 47: {
+ goto st2;
+ }
+ }
+ {
+ goto st14;
+ }
+ st16:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof16;
+ st_case_16:
+ if ( ( (*( p))) == 10 ) {
+ goto st2;
+ }
+ {
+ goto st16;
+ }
+ st_out:
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+ #line 446 "parser.rl"
+
+
+ if(cs >= JSON_array_first_final) {
+ return p + 1;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ return NULL;
+ }
+}
+
+static const size_t MAX_STACK_BUFFER_SIZE = 128;
+static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize)
+{
+ VALUE result = Qnil;
+ size_t bufferSize = stringEnd - string;
+ char *p = string, *pe = string, *unescape, *bufferStart, *buffer;
+ int unescape_len;
+ char buf[4];
+
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ bufferStart = buffer = ALLOC_N(char, bufferSize);
+ } else {
+ bufferStart = buffer = ALLOCA_N(char, bufferSize);
+ }
+
+ while (pe < stringEnd) {
+ if (*pe == '\\') {
+ unescape = (char *) "?";
+ unescape_len = 1;
+ if (pe > p) {
+ MEMCPY(buffer, p, char, pe - p);
+ buffer += pe - p;
+ }
+ switch (*++pe) {
+ case 'n':
+ unescape = (char *) "\n";
+ break;
+ case 'r':
+ unescape = (char *) "\r";
+ break;
+ case 't':
+ unescape = (char *) "\t";
+ break;
+ case '"':
+ unescape = (char *) "\"";
+ break;
+ case '\\':
+ unescape = (char *) "\\";
+ break;
+ case 'b':
+ unescape = (char *) "\b";
+ break;
+ case 'f':
+ unescape = (char *) "\f";
+ break;
+ case 'u':
+ if (pe > stringEnd - 4) {
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p
+ );
+ } else {
+ UTF32 ch = unescape_unicode((unsigned char *) ++pe);
+ pe += 3;
+ if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
+ pe++;
+ if (pe > stringEnd - 6) {
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete surrogate pair at '%s'", __LINE__, p
+ );
+ }
+ if (pe[0] == '\\' && pe[1] == 'u') {
+ UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
+ ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
+ | (sur & 0x3FF));
+ pe += 5;
+ } else {
+ unescape = (char *) "?";
+ break;
+ }
+ }
+ unescape_len = convert_UTF32_to_UTF8(buf, ch);
+ unescape = buf;
+ }
+ break;
+ default:
+ p = pe;
+ continue;
+ }
+ MEMCPY(buffer, unescape, char, unescape_len);
+ buffer += unescape_len;
+ p = ++pe;
+ } else {
+ pe++;
+ }
+ }
+
+ if (pe > p) {
+ MEMCPY(buffer, p, char, pe - p);
+ buffer += pe - p;
+ }
+
+ # ifdef HAVE_RB_ENC_INTERNED_STR
+ if (intern) {
+ result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding());
+ } else {
+ result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
+ }
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+ # else
+ result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
+
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+
+ if (intern) {
+ # if STR_UMINUS_DEDUPE_FROZEN
+ // Starting from MRI 2.8 it is preferable to freeze the string
+ // before deduplication so that it can be interned directly
+ // otherwise it would be duplicated first which is wasteful.
+ result = rb_funcall(rb_str_freeze(result), i_uminus, 0);
+ # elif STR_UMINUS_DEDUPE
+ // MRI 2.5 and older do not deduplicate strings that are already
+ // frozen.
+ result = rb_funcall(result, i_uminus, 0);
+ # else
+ result = rb_str_freeze(result);
+ # endif
+ }
+ # endif
+
+ if (symbolize) {
+ result = rb_str_intern(result);
+ }
+
+ return result;
+}
+
+
+enum {JSON_string_start = 1};
+enum {JSON_string_first_final = 8};
+enum {JSON_string_error = 0};
+
+enum {JSON_string_en_main = 1};
+
+static const char MAYBE_UNUSED(_JSON_string_nfa_targs)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_string_nfa_offsets)[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_string_nfa_push_actions)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_string_nfa_pop_trans)[] = {
+ 0, 0
+};
+
+
+#line 612 "parser.rl"
+
+
+static int
+match_i(VALUE regexp, VALUE klass, VALUE memo)
+{
+ if (regexp == Qundef) return ST_STOP;
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
+ RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
+ rb_ary_push(memo, klass);
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
+{
+ int cs = EVIL;
+ VALUE match_string;
+
+
+ {
+ cs = (int)JSON_string_start;
+ }
+
+ #line 632 "parser.rl"
+
+ json->memo = p;
+
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+ case 1:
+ goto st_case_1;
+ case 0:
+ goto st_case_0;
+ case 2:
+ goto st_case_2;
+ case 8:
+ goto st_case_8;
+ case 3:
+ goto st_case_3;
+ case 4:
+ goto st_case_4;
+ case 5:
+ goto st_case_5;
+ case 6:
+ goto st_case_6;
+ case 7:
+ goto st_case_7;
+ }
+ goto st_out;
+ st_case_1:
+ if ( ( (*( p))) == 34 ) {
+ goto st2;
+ }
+ {
+ goto st0;
+ }
+ st_case_0:
+ st0:
+ cs = 0;
+ goto _out;
+ st2:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof2;
+ st_case_2:
+ switch( ( (*( p))) ) {
+ case 34: {
+ goto ctr2;
+ }
+ case 92: {
+ goto st3;
+ }
+ }
+ if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) {
+ goto st0;
+ }
+ {
+ goto st2;
+ }
+ ctr2:
+ {
+ #line 599 "parser.rl"
+
+ *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names);
+ if (NIL_P(*result)) {
+ {p = p - 1; }
+ {p+= 1; cs = 8; goto _out;}
+ } else {
+ {p = (( p + 1))-1;}
+
+ }
+ }
+ {
+ #line 609 "parser.rl"
+ {p = p - 1; } {p+= 1; cs = 8; goto _out;} }
+
+ goto st8;
+ st8:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof8;
+ st_case_8:
+ {
+ goto st0;
+ }
+ st3:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof3;
+ st_case_3:
+ if ( ( (*( p))) == 117 ) {
+ goto st4;
+ }
+ if ( 0 <= (signed char)(*(p)) && (*(p)) <= 31 ) {
+ goto st0;
+ }
+ {
+ goto st2;
+ }
+ st4:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof4;
+ st_case_4:
+ if ( ( (*( p))) < 65 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st5;
+ }
+ } else if ( ( (*( p))) > 70 ) {
+ if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
+ goto st5;
+ }
+ } else {
+ goto st5;
+ }
+ {
+ goto st0;
+ }
+ st5:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof5;
+ st_case_5:
+ if ( ( (*( p))) < 65 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st6;
+ }
+ } else if ( ( (*( p))) > 70 ) {
+ if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
+ goto st6;
+ }
+ } else {
+ goto st6;
+ }
+ {
+ goto st0;
+ }
+ st6:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof6;
+ st_case_6:
+ if ( ( (*( p))) < 65 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st7;
+ }
+ } else if ( ( (*( p))) > 70 ) {
+ if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
+ goto st7;
+ }
+ } else {
+ goto st7;
+ }
+ {
+ goto st0;
+ }
+ st7:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof7;
+ st_case_7:
+ if ( ( (*( p))) < 65 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto st2;
+ }
+ } else if ( ( (*( p))) > 70 ) {
+ if ( 97 <= ( (*( p))) && ( (*( p))) <= 102 ) {
+ goto st2;
+ }
+ } else {
+ goto st2;
+ }
+ {
+ goto st0;
+ }
+ st_out:
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+ #line 634 "parser.rl"
+
+
+ if (json->create_additions && RTEST(match_string = json->match_string)) {
+ VALUE klass;
+ VALUE memo = rb_ary_new2(2);
+ rb_ary_push(memo, *result);
+ rb_hash_foreach(match_string, match_i, memo);
+ klass = rb_ary_entry(memo, 1);
+ if (RTEST(klass)) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
+
+ if (cs >= JSON_string_first_final) {
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+/*
+* Document-class: JSON::Ext::Parser
+*
+* This is the JSON parser implemented as a C extension. It can be configured
+* to be used by setting
+*
+* JSON.parser = JSON::Ext::Parser
+*
+* with the method parser= in JSON.
+*
+*/
+
+static VALUE convert_encoding(VALUE source)
+{
+ #ifdef HAVE_RUBY_ENCODING_H
+ rb_encoding *enc = rb_enc_get(source);
+ if (enc == rb_ascii8bit_encoding()) {
+ if (OBJ_FROZEN(source)) {
+ source = rb_str_dup(source);
+ }
+ FORCE_UTF8(source);
+ } else {
+ source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
+ }
+ #endif
+ return source;
+}
+
+/*
+* call-seq: new(source, opts => {})
+*
+* Creates a new JSON::Ext::Parser instance for the string _source_.
+*
+* Creates a new JSON::Ext::Parser instance for the string _source_.
+*
+* It will be configured by the _opts_ hash. _opts_ can have the following
+* keys:
+*
+* _opts_ can have the following keys:
+* * *max_nesting*: The maximum depth of nesting allowed in the parsed data
+* structures. Disable depth checking with :max_nesting => false|nil|0, it
+* defaults to 100.
+* * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
+* defiance of RFC 4627 to be parsed by the Parser. This option defaults to
+* false.
+* * *symbolize_names*: If set to true, returns symbols for the names
+* (keys) in a JSON object. Otherwise strings are returned, which is
+* also the default. It's not possible to use this option in
+* conjunction with the *create_additions* option.
+* * *create_additions*: If set to false, the Parser doesn't create
+* additions even if a matching class and create_id was found. This option
+* defaults to false.
+* * *object_class*: Defaults to Hash
+* * *array_class*: Defaults to Array
+*/
+static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE source, opts;
+ GET_PARSER_INIT;
+
+ if (json->Vsource) {
+ rb_raise(rb_eTypeError, "already initialized instance");
+ }
+ #ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
+ rb_scan_args(argc, argv, "1:", &source, &opts);
+ #else
+ rb_scan_args(argc, argv, "11", &source, &opts);
+ #endif
+ if (!NIL_P(opts)) {
+ #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
+ opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
+ if (NIL_P(opts)) {
+ rb_raise(rb_eArgError, "opts needs to be like a hash");
+ } else {
+ #endif
+ VALUE tmp = ID2SYM(i_max_nesting);
+ if (option_given_p(opts, tmp)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
+ if (RTEST(max_nesting)) {
+ Check_Type(max_nesting, T_FIXNUM);
+ json->max_nesting = FIX2INT(max_nesting);
+ } else {
+ json->max_nesting = 0;
+ }
+ } else {
+ json->max_nesting = 100;
+ }
+ tmp = ID2SYM(i_allow_nan);
+ if (option_given_p(opts, tmp)) {
+ json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->allow_nan = 0;
+ }
+ tmp = ID2SYM(i_symbolize_names);
+ if (option_given_p(opts, tmp)) {
+ json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->symbolize_names = 0;
+ }
+ tmp = ID2SYM(i_freeze);
+ if (option_given_p(opts, tmp)) {
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->freeze = 0;
+ }
+ tmp = ID2SYM(i_create_additions);
+ if (option_given_p(opts, tmp)) {
+ json->create_additions = RTEST(rb_hash_aref(opts, tmp));
+ } else {
+ json->create_additions = 0;
+ }
+ if (json->symbolize_names && json->create_additions) {
+ rb_raise(rb_eArgError,
+ "options :symbolize_names and :create_additions cannot be "
+ " used in conjunction");
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ json->create_id = rb_hash_aref(opts, tmp);
+ } else {
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ }
+ tmp = ID2SYM(i_object_class);
+ if (option_given_p(opts, tmp)) {
+ json->object_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->object_class = Qnil;
+ }
+ tmp = ID2SYM(i_array_class);
+ if (option_given_p(opts, tmp)) {
+ json->array_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->array_class = Qnil;
+ }
+ tmp = ID2SYM(i_decimal_class);
+ if (option_given_p(opts, tmp)) {
+ json->decimal_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->decimal_class = Qnil;
+ }
+ tmp = ID2SYM(i_match_string);
+ if (option_given_p(opts, tmp)) {
+ VALUE match_string = rb_hash_aref(opts, tmp);
+ json->match_string = RTEST(match_string) ? match_string : Qnil;
+ } else {
+ json->match_string = Qnil;
+ }
+ #ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
+ }
+ #endif
+ } else {
+ json->max_nesting = 100;
+ json->allow_nan = 0;
+ json->create_additions = 0;
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ json->object_class = Qnil;
+ json->array_class = Qnil;
+ json->decimal_class = Qnil;
+ }
+ source = convert_encoding(StringValue(source));
+ StringValue(source);
+ json->len = RSTRING_LEN(source);
+ json->source = RSTRING_PTR(source);;
+ json->Vsource = source;
+ return self;
+}
+
+
+enum {JSON_start = 1};
+enum {JSON_first_final = 10};
+enum {JSON_error = 0};
+
+enum {JSON_en_main = 1};
+
+static const char MAYBE_UNUSED(_JSON_nfa_targs)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_nfa_offsets)[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_nfa_push_actions)[] = {
+ 0, 0
+};
+
+static const char MAYBE_UNUSED(_JSON_nfa_pop_trans)[] = {
+ 0, 0
+};
+
+
+#line 835 "parser.rl"
+
+
+/*
+* call-seq: parse()
+*
+* Parses the current JSON text _source_ and returns the complete data
+* structure as a result.
+*/
+static VALUE cParser_parse(VALUE self)
+{
+ char *p, *pe;
+ int cs = EVIL;
+ VALUE result = Qnil;
+ GET_PARSER;
+
+
+ {
+ cs = (int)JSON_start;
+ }
+
+ #line 851 "parser.rl"
+
+ p = json->source;
+ pe = p + json->len;
+
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+ case 1:
+ goto st_case_1;
+ case 0:
+ goto st_case_0;
+ case 10:
+ goto st_case_10;
+ case 2:
+ goto st_case_2;
+ case 3:
+ goto st_case_3;
+ case 4:
+ goto st_case_4;
+ case 5:
+ goto st_case_5;
+ case 6:
+ goto st_case_6;
+ case 7:
+ goto st_case_7;
+ case 8:
+ goto st_case_8;
+ case 9:
+ goto st_case_9;
+ }
+ goto st_out;
+ st1:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof1;
+ st_case_1:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st1;
+ }
+ case 32: {
+ goto st1;
+ }
+ case 34: {
+ goto ctr2;
+ }
+ case 45: {
+ goto ctr2;
+ }
+ case 47: {
+ goto st6;
+ }
+ case 73: {
+ goto ctr2;
+ }
+ case 78: {
+ goto ctr2;
+ }
+ case 91: {
+ goto ctr2;
+ }
+ case 102: {
+ goto ctr2;
+ }
+ case 110: {
+ goto ctr2;
+ }
+ case 116: {
+ goto ctr2;
+ }
+ case 123: {
+ goto ctr2;
+ }
+ }
+ if ( ( (*( p))) > 10 ) {
+ if ( 48 <= ( (*( p))) && ( (*( p))) <= 57 ) {
+ goto ctr2;
+ }
+ } else if ( ( (*( p))) >= 9 ) {
+ goto st1;
+ }
+ {
+ goto st0;
+ }
+ st_case_0:
+ st0:
+ cs = 0;
+ goto _out;
+ ctr2:
+ {
+ #line 827 "parser.rl"
+
+ char *np = JSON_parse_value(json, p, pe, &result, 0);
+ if (np == NULL) { {p = p - 1; } {p+= 1; cs = 10; goto _out;} } else {p = (( np))-1;}
+
+ }
+
+ goto st10;
+ st10:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof10;
+ st_case_10:
+ switch( ( (*( p))) ) {
+ case 13: {
+ goto st10;
+ }
+ case 32: {
+ goto st10;
+ }
+ case 47: {
+ goto st2;
+ }
+ }
+ if ( 9 <= ( (*( p))) && ( (*( p))) <= 10 ) {
+ goto st10;
+ }
+ {
+ goto st0;
+ }
+ st2:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof2;
+ st_case_2:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st3;
+ }
+ case 47: {
+ goto st5;
+ }
+ }
+ {
+ goto st0;
+ }
+ st3:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof3;
+ st_case_3:
+ if ( ( (*( p))) == 42 ) {
+ goto st4;
+ }
+ {
+ goto st3;
+ }
+ st4:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof4;
+ st_case_4:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st4;
+ }
+ case 47: {
+ goto st10;
+ }
+ }
+ {
+ goto st3;
+ }
+ st5:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof5;
+ st_case_5:
+ if ( ( (*( p))) == 10 ) {
+ goto st10;
+ }
+ {
+ goto st5;
+ }
+ st6:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof6;
+ st_case_6:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st7;
+ }
+ case 47: {
+ goto st9;
+ }
+ }
+ {
+ goto st0;
+ }
+ st7:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof7;
+ st_case_7:
+ if ( ( (*( p))) == 42 ) {
+ goto st8;
+ }
+ {
+ goto st7;
+ }
+ st8:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof8;
+ st_case_8:
+ switch( ( (*( p))) ) {
+ case 42: {
+ goto st8;
+ }
+ case 47: {
+ goto st1;
+ }
+ }
+ {
+ goto st7;
+ }
+ st9:
+ p+= 1;
+ if ( p == pe )
+ goto _test_eof9;
+ st_case_9:
+ if ( ( (*( p))) == 10 ) {
+ goto st1;
+ }
+ {
+ goto st9;
+ }
+ st_out:
+ _test_eof1: cs = 1; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+ #line 854 "parser.rl"
+
+
+ if (cs >= JSON_first_final && p == pe) {
+ return result;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ return Qnil;
+ }
+}
+
+static void JSON_mark(void *ptr)
+{
+ JSON_Parser *json = ptr;
+ rb_gc_mark_maybe(json->Vsource);
+ rb_gc_mark_maybe(json->create_id);
+ rb_gc_mark_maybe(json->object_class);
+ rb_gc_mark_maybe(json->array_class);
+ rb_gc_mark_maybe(json->decimal_class);
+ rb_gc_mark_maybe(json->match_string);
+}
+
+static void JSON_free(void *ptr)
+{
+ JSON_Parser *json = ptr;
+ fbuffer_free(json->fbuffer);
+ ruby_xfree(json);
+}
+
+static size_t JSON_memsize(const void *ptr)
+{
+ const JSON_Parser *json = ptr;
+ return sizeof(*json) + FBUFFER_CAPA(json->fbuffer);
+}
+
+#ifdef NEW_TYPEDDATA_WRAPPER
+static const rb_data_type_t JSON_Parser_type = {
+ "JSON/Parser",
+ {JSON_mark, JSON_free, JSON_memsize,},
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY,
+ #endif
+};
+#endif
+
+static VALUE cJSON_parser_s_allocate(VALUE klass)
+{
+ JSON_Parser *json;
+ VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
+ json->fbuffer = fbuffer_alloc(0);
+ return obj;
+}
+
+/*
+* call-seq: source()
+*
+* Returns a copy of the current _source_ string, that was used to construct
+* this Parser.
+*/
+static VALUE cParser_source(VALUE self)
+{
+ GET_PARSER;
+ return rb_str_dup(json->Vsource);
+}
+
+void Init_parser(void)
+{
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+ #endif
+
+ #undef rb_intern
+ rb_require("json/common");
+ mJSON = rb_define_module("JSON");
+ mExt = rb_define_module_under(mJSON, "Ext");
+ cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
+ eParserError = rb_path2class("JSON::ParserError");
+ eNestingError = rb_path2class("JSON::NestingError");
+ rb_gc_register_mark_object(eParserError);
+ rb_gc_register_mark_object(eNestingError);
+ rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
+ rb_define_method(cParser, "initialize", cParser_initialize, -1);
+ rb_define_method(cParser, "parse", cParser_parse, 0);
+ rb_define_method(cParser, "source", cParser_source, 0);
+
+ CNaN = rb_const_get(mJSON, rb_intern("NaN"));
+ rb_gc_register_mark_object(CNaN);
+
+ CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
+ rb_gc_register_mark_object(CInfinity);
+
+ CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
+ rb_gc_register_mark_object(CMinusInfinity);
+
+ i_json_creatable_p = rb_intern("json_creatable?");
+ i_json_create = rb_intern("json_create");
+ i_create_id = rb_intern("create_id");
+ i_create_additions = rb_intern("create_additions");
+ i_chr = rb_intern("chr");
+ i_max_nesting = rb_intern("max_nesting");
+ i_allow_nan = rb_intern("allow_nan");
+ i_symbolize_names = rb_intern("symbolize_names");
+ i_object_class = rb_intern("object_class");
+ i_array_class = rb_intern("array_class");
+ i_decimal_class = rb_intern("decimal_class");
+ i_match = rb_intern("match");
+ i_match_string = rb_intern("match_string");
+ i_key_p = rb_intern("key?");
+ i_deep_const_get = rb_intern("deep_const_get");
+ i_aset = rb_intern("[]=");
+ i_aref = rb_intern("[]");
+ i_leftshift = rb_intern("<<");
+ i_new = rb_intern("new");
+ i_try_convert = rb_intern("try_convert");
+ i_freeze = rb_intern("freeze");
+ i_uminus = rb_intern("-@");
+}
+
+/*
+* Local variables:
+* mode: c
+* c-file-style: ruby
+* indent-tabs-mode: nil
+* End:
+*/
diff --git a/ext/json/parser/parser.h b/ext/json/parser/parser.h
new file mode 100644
index 0000000000..92ed3fdc5d
--- /dev/null
+++ b/ext/json/parser/parser.h
@@ -0,0 +1,96 @@
+#ifndef _PARSER_H_
+#define _PARSER_H_
+
+#include "ruby.h"
+
+#ifndef HAVE_RUBY_RE_H
+#include "re.h"
+#endif
+
+#ifdef HAVE_RUBY_ST_H
+#include "ruby/st.h"
+#else
+#include "st.h"
+#endif
+
+#ifndef MAYBE_UNUSED
+# define MAYBE_UNUSED(x) x
+#endif
+
+#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
+
+/* unicode */
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+typedef struct JSON_ParserStruct {
+ VALUE Vsource;
+ char *source;
+ long len;
+ char *memo;
+ VALUE create_id;
+ int max_nesting;
+ int allow_nan;
+ int parsing_name;
+ int symbolize_names;
+ int freeze;
+ VALUE object_class;
+ VALUE array_class;
+ VALUE decimal_class;
+ int create_additions;
+ VALUE match_string;
+ FBuffer *fbuffer;
+} JSON_Parser;
+
+#define GET_PARSER \
+ GET_PARSER_INIT; \
+ if (!json->Vsource) rb_raise(rb_eTypeError, "uninitialized instance")
+#define GET_PARSER_INIT \
+ JSON_Parser *json; \
+ TypedData_Get_Struct(self, JSON_Parser, &JSON_Parser_type, json)
+
+#define MinusInfinity "-Infinity"
+#define EVIL 0x666
+
+static UTF32 unescape_unicode(const unsigned char *p);
+static int convert_UTF32_to_UTF8(char *buf, UTF32 ch);
+static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting);
+static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting);
+static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result);
+static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result);
+static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting);
+static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize);
+static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
+static VALUE convert_encoding(VALUE source);
+static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self);
+static VALUE cParser_parse(VALUE self);
+static void JSON_mark(void *json);
+static void JSON_free(void *json);
+static VALUE cJSON_parser_s_allocate(VALUE klass);
+static VALUE cParser_source(VALUE self);
+#ifndef ZALLOC
+#define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type)))
+static inline void *ruby_zalloc(size_t n)
+{
+ void *p = ruby_xmalloc(n);
+ memset(p, 0, n);
+ return p;
+}
+#endif
+#ifdef TypedData_Make_Struct
+static const rb_data_type_t JSON_Parser_type;
+#define NEW_TYPEDDATA_WRAPPER 1
+#else
+#define TypedData_Make_Struct(klass, type, ignore, json) Data_Make_Struct(klass, type, NULL, JSON_free, json)
+#define TypedData_Get_Struct(self, JSON_Parser, ignore, json) Data_Get_Struct(self, JSON_Parser, json)
+#endif
+
+#endif
diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl
new file mode 100644
index 0000000000..f7be1a5acc
--- /dev/null
+++ b/ext/json/parser/parser.rl
@@ -0,0 +1,977 @@
+#include "../fbuffer/fbuffer.h"
+#include "parser.h"
+
+#if defined HAVE_RUBY_ENCODING_H
+# define EXC_ENCODING rb_utf8_encoding(),
+# ifndef HAVE_RB_ENC_RAISE
+static void
+enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
+{
+ va_list args;
+ VALUE mesg;
+
+ va_start(args, fmt);
+ mesg = rb_enc_vsprintf(enc, fmt, args);
+ va_end(args);
+
+ rb_exc_raise(rb_exc_new3(exc, mesg));
+}
+# define rb_enc_raise enc_raise
+# endif
+#else
+# define EXC_ENCODING /* nothing */
+# define rb_enc_raise rb_raise
+#endif
+
+/* unicode */
+
+static const signed char digit_values[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+};
+
+static UTF32 unescape_unicode(const unsigned char *p)
+{
+ signed char b;
+ UTF32 result = 0;
+ b = digit_values[p[0]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[1]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[2]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ b = digit_values[p[3]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | (unsigned char)b;
+ return result;
+}
+
+static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
+{
+ int len = 1;
+ if (ch <= 0x7F) {
+ buf[0] = (char) ch;
+ } else if (ch <= 0x07FF) {
+ buf[0] = (char) ((ch >> 6) | 0xC0);
+ buf[1] = (char) ((ch & 0x3F) | 0x80);
+ len++;
+ } else if (ch <= 0xFFFF) {
+ buf[0] = (char) ((ch >> 12) | 0xE0);
+ buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[2] = (char) ((ch & 0x3F) | 0x80);
+ len += 2;
+ } else if (ch <= 0x1fffff) {
+ buf[0] =(char) ((ch >> 18) | 0xF0);
+ buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
+ buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[3] =(char) ((ch & 0x3F) | 0x80);
+ len += 3;
+ } else {
+ buf[0] = '?';
+ }
+ return len;
+}
+
+static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
+static VALUE CNaN, CInfinity, CMinusInfinity;
+
+static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
+ i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
+ i_object_class, i_array_class, i_decimal_class, i_key_p,
+ i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
+ i_leftshift, i_new, i_try_convert, i_freeze, i_uminus;
+
+%%{
+ machine JSON_common;
+
+ cr = '\n';
+ cr_neg = [^\n];
+ ws = [ \t\r\n];
+ c_comment = '/*' ( any* - (any* '*/' any* ) ) '*/';
+ cpp_comment = '//' cr_neg* cr;
+ comment = c_comment | cpp_comment;
+ ignore = ws | comment;
+ name_separator = ':';
+ value_separator = ',';
+ Vnull = 'null';
+ Vfalse = 'false';
+ Vtrue = 'true';
+ VNaN = 'NaN';
+ VInfinity = 'Infinity';
+ VMinusInfinity = '-Infinity';
+ begin_value = [nft\"\-\[\{NI] | digit;
+ begin_object = '{';
+ end_object = '}';
+ begin_array = '[';
+ end_array = ']';
+ begin_string = '"';
+ begin_name = begin_string;
+ begin_number = digit | '-';
+}%%
+
+%%{
+ machine JSON_object;
+ include JSON_common;
+
+ write data;
+
+ action parse_value {
+ VALUE v = Qnil;
+ char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting);
+ if (np == NULL) {
+ fhold; fbreak;
+ } else {
+ if (NIL_P(json->object_class)) {
+ OBJ_FREEZE(last_name);
+ rb_hash_aset(*result, last_name, v);
+ } else {
+ rb_funcall(*result, i_aset, 2, last_name, v);
+ }
+ fexec np;
+ }
+ }
+
+ action parse_name {
+ char *np;
+ json->parsing_name = 1;
+ np = JSON_parse_string(json, fpc, pe, &last_name);
+ json->parsing_name = 0;
+ if (np == NULL) { fhold; fbreak; } else fexec np;
+ }
+
+ action exit { fhold; fbreak; }
+
+ pair = ignore* begin_name >parse_name ignore* name_separator ignore* begin_value >parse_value;
+ next_pair = ignore* value_separator pair;
+
+ main := (
+ begin_object
+ (pair (next_pair)*)? ignore*
+ end_object
+ ) @exit;
+}%%
+
+static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
+{
+ int cs = EVIL;
+ VALUE last_name = Qnil;
+ VALUE object_class = json->object_class;
+
+ if (json->max_nesting && current_nesting > json->max_nesting) {
+ rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
+ }
+
+ *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
+
+ %% write init;
+ %% write exec;
+
+ if (cs >= JSON_object_first_final) {
+ if (json->create_additions) {
+ VALUE klassname;
+ if (NIL_P(json->object_class)) {
+ klassname = rb_hash_aref(*result, json->create_id);
+ } else {
+ klassname = rb_funcall(*result, i_aref, 1, json->create_id);
+ }
+ if (!NIL_P(klassname)) {
+ VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
+ }
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+
+%%{
+ machine JSON_value;
+ include JSON_common;
+
+ write data;
+
+ action parse_null {
+ *result = Qnil;
+ }
+ action parse_false {
+ *result = Qfalse;
+ }
+ action parse_true {
+ *result = Qtrue;
+ }
+ action parse_nan {
+ if (json->allow_nan) {
+ *result = CNaN;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
+ }
+ }
+ action parse_infinity {
+ if (json->allow_nan) {
+ *result = CInfinity;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
+ }
+ }
+ action parse_string {
+ char *np = JSON_parse_string(json, fpc, pe, result);
+ if (np == NULL) { fhold; fbreak; } else fexec np;
+ }
+
+ action parse_number {
+ char *np;
+ if(pe > fpc + 8 && !strncmp(MinusInfinity, fpc, 9)) {
+ if (json->allow_nan) {
+ *result = CMinusInfinity;
+ fexec p + 10;
+ fhold; fbreak;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ }
+ }
+ np = JSON_parse_float(json, fpc, pe, result);
+ if (np != NULL) fexec np;
+ np = JSON_parse_integer(json, fpc, pe, result);
+ if (np != NULL) fexec np;
+ fhold; fbreak;
+ }
+
+ action parse_array {
+ char *np;
+ np = JSON_parse_array(json, fpc, pe, result, current_nesting + 1);
+ if (np == NULL) { fhold; fbreak; } else fexec np;
+ }
+
+ action parse_object {
+ char *np;
+ np = JSON_parse_object(json, fpc, pe, result, current_nesting + 1);
+ if (np == NULL) { fhold; fbreak; } else fexec np;
+ }
+
+ action exit { fhold; fbreak; }
+
+main := ignore* (
+ Vnull @parse_null |
+ Vfalse @parse_false |
+ Vtrue @parse_true |
+ VNaN @parse_nan |
+ VInfinity @parse_infinity |
+ begin_number >parse_number |
+ begin_string >parse_string |
+ begin_array >parse_array |
+ begin_object >parse_object
+ ) ignore* %*exit;
+}%%
+
+static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
+{
+ int cs = EVIL;
+
+ %% write init;
+ %% write exec;
+
+ if (json->freeze) {
+ OBJ_FREEZE(*result);
+ }
+
+ if (cs >= JSON_value_first_final) {
+ return p;
+ } else {
+ return NULL;
+ }
+}
+
+%%{
+ machine JSON_integer;
+
+ write data;
+
+ action exit { fhold; fbreak; }
+
+ main := '-'? ('0' | [1-9][0-9]*) (^[0-9]? @exit);
+}%%
+
+static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
+{
+ int cs = EVIL;
+
+ %% write init;
+ json->memo = p;
+ %% write exec;
+
+ if (cs >= JSON_integer_first_final) {
+ long len = p - json->memo;
+ fbuffer_clear(json->fbuffer);
+ fbuffer_append(json->fbuffer, json->memo, len);
+ fbuffer_append_char(json->fbuffer, '\0');
+ *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10);
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+%%{
+ machine JSON_float;
+ include JSON_common;
+
+ write data;
+
+ action exit { fhold; fbreak; }
+
+ main := '-'? (
+ (('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?)
+ | (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+))
+ ) (^[0-9Ee.\-]? @exit );
+}%%
+
+static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
+{
+ int cs = EVIL;
+
+ %% write init;
+ json->memo = p;
+ %% write exec;
+
+ if (cs >= JSON_float_first_final) {
+ VALUE mod = Qnil;
+ ID method_id = 0;
+ if (rb_respond_to(json->decimal_class, i_try_convert)) {
+ mod = json->decimal_class;
+ method_id = i_try_convert;
+ } else if (rb_respond_to(json->decimal_class, i_new)) {
+ mod = json->decimal_class;
+ method_id = i_new;
+ } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) {
+ VALUE name = rb_class_name(json->decimal_class);
+ const char *name_cstr = RSTRING_PTR(name);
+ const char *last_colon = strrchr(name_cstr, ':');
+ if (last_colon) {
+ const char *mod_path_end = last_colon - 1;
+ VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
+ mod = rb_path_to_class(mod_path);
+
+ const char *method_name_beg = last_colon + 1;
+ long before_len = method_name_beg - name_cstr;
+ long len = RSTRING_LEN(name) - before_len;
+ VALUE method_name = rb_str_substr(name, before_len, len);
+ method_id = SYM2ID(rb_str_intern(method_name));
+ } else {
+ mod = rb_mKernel;
+ method_id = SYM2ID(rb_str_intern(name));
+ }
+ }
+
+ long len = p - json->memo;
+ fbuffer_clear(json->fbuffer);
+ fbuffer_append(json->fbuffer, json->memo, len);
+ fbuffer_append_char(json->fbuffer, '\0');
+
+ if (method_id) {
+ VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+ *result = rb_funcallv(mod, method_id, 1, &text);
+ } else {
+ *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+ }
+
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+
+%%{
+ machine JSON_array;
+ include JSON_common;
+
+ write data;
+
+ action parse_value {
+ VALUE v = Qnil;
+ char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting);
+ if (np == NULL) {
+ fhold; fbreak;
+ } else {
+ if (NIL_P(json->array_class)) {
+ rb_ary_push(*result, v);
+ } else {
+ rb_funcall(*result, i_leftshift, 1, v);
+ }
+ fexec np;
+ }
+ }
+
+ action exit { fhold; fbreak; }
+
+ next_element = value_separator ignore* begin_value >parse_value;
+
+ main := begin_array ignore*
+ ((begin_value >parse_value ignore*)
+ (ignore* next_element ignore*)*)?
+ end_array @exit;
+}%%
+
+static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
+{
+ int cs = EVIL;
+ VALUE array_class = json->array_class;
+
+ if (json->max_nesting && current_nesting > json->max_nesting) {
+ rb_raise(eNestingError, "nesting of %d is too deep", current_nesting);
+ }
+ *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
+
+ %% write init;
+ %% write exec;
+
+ if(cs >= JSON_array_first_final) {
+ return p + 1;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ return NULL;
+ }
+}
+
+static const size_t MAX_STACK_BUFFER_SIZE = 128;
+static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize)
+{
+ VALUE result = Qnil;
+ size_t bufferSize = stringEnd - string;
+ char *p = string, *pe = string, *unescape, *bufferStart, *buffer;
+ int unescape_len;
+ char buf[4];
+
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ bufferStart = buffer = ALLOC_N(char, bufferSize);
+ } else {
+ bufferStart = buffer = ALLOCA_N(char, bufferSize);
+ }
+
+ while (pe < stringEnd) {
+ if (*pe == '\\') {
+ unescape = (char *) "?";
+ unescape_len = 1;
+ if (pe > p) {
+ MEMCPY(buffer, p, char, pe - p);
+ buffer += pe - p;
+ }
+ switch (*++pe) {
+ case 'n':
+ unescape = (char *) "\n";
+ break;
+ case 'r':
+ unescape = (char *) "\r";
+ break;
+ case 't':
+ unescape = (char *) "\t";
+ break;
+ case '"':
+ unescape = (char *) "\"";
+ break;
+ case '\\':
+ unescape = (char *) "\\";
+ break;
+ case 'b':
+ unescape = (char *) "\b";
+ break;
+ case 'f':
+ unescape = (char *) "\f";
+ break;
+ case 'u':
+ if (pe > stringEnd - 4) {
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete unicode character escape sequence at '%s'", __LINE__, p
+ );
+ } else {
+ UTF32 ch = unescape_unicode((unsigned char *) ++pe);
+ pe += 3;
+ if (UNI_SUR_HIGH_START == (ch & 0xFC00)) {
+ pe++;
+ if (pe > stringEnd - 6) {
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+ rb_enc_raise(
+ EXC_ENCODING eParserError,
+ "%u: incomplete surrogate pair at '%s'", __LINE__, p
+ );
+ }
+ if (pe[0] == '\\' && pe[1] == 'u') {
+ UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
+ ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
+ | (sur & 0x3FF));
+ pe += 5;
+ } else {
+ unescape = (char *) "?";
+ break;
+ }
+ }
+ unescape_len = convert_UTF32_to_UTF8(buf, ch);
+ unescape = buf;
+ }
+ break;
+ default:
+ p = pe;
+ continue;
+ }
+ MEMCPY(buffer, unescape, char, unescape_len);
+ buffer += unescape_len;
+ p = ++pe;
+ } else {
+ pe++;
+ }
+ }
+
+ if (pe > p) {
+ MEMCPY(buffer, p, char, pe - p);
+ buffer += pe - p;
+ }
+
+# ifdef HAVE_RB_ENC_INTERNED_STR
+ if (intern) {
+ result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding());
+ } else {
+ result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
+ }
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+# else
+ result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart));
+
+ if (bufferSize > MAX_STACK_BUFFER_SIZE) {
+ free(bufferStart);
+ }
+
+ if (intern) {
+ # if STR_UMINUS_DEDUPE_FROZEN
+ // Starting from MRI 2.8 it is preferable to freeze the string
+ // before deduplication so that it can be interned directly
+ // otherwise it would be duplicated first which is wasteful.
+ result = rb_funcall(rb_str_freeze(result), i_uminus, 0);
+ # elif STR_UMINUS_DEDUPE
+ // MRI 2.5 and older do not deduplicate strings that are already
+ // frozen.
+ result = rb_funcall(result, i_uminus, 0);
+ # else
+ result = rb_str_freeze(result);
+ # endif
+ }
+# endif
+
+ if (symbolize) {
+ result = rb_str_intern(result);
+ }
+
+ return result;
+}
+
+%%{
+ machine JSON_string;
+ include JSON_common;
+
+ write data;
+
+ action parse_string {
+ *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names);
+ if (NIL_P(*result)) {
+ fhold;
+ fbreak;
+ } else {
+ fexec p + 1;
+ }
+ }
+
+ action exit { fhold; fbreak; }
+
+ main := '"' ((^([\"\\] | 0..0x1f) | '\\'[\"\\/bfnrt] | '\\u'[0-9a-fA-F]{4} | '\\'^([\"\\/bfnrtu]|0..0x1f))* %parse_string) '"' @exit;
+}%%
+
+static int
+match_i(VALUE regexp, VALUE klass, VALUE memo)
+{
+ if (regexp == Qundef) return ST_STOP;
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
+ RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
+ rb_ary_push(memo, klass);
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
+{
+ int cs = EVIL;
+ VALUE match_string;
+
+ %% write init;
+ json->memo = p;
+ %% write exec;
+
+ if (json->create_additions && RTEST(match_string = json->match_string)) {
+ VALUE klass;
+ VALUE memo = rb_ary_new2(2);
+ rb_ary_push(memo, *result);
+ rb_hash_foreach(match_string, match_i, memo);
+ klass = rb_ary_entry(memo, 1);
+ if (RTEST(klass)) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
+
+ if (cs >= JSON_string_first_final) {
+ return p + 1;
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Document-class: JSON::Ext::Parser
+ *
+ * This is the JSON parser implemented as a C extension. It can be configured
+ * to be used by setting
+ *
+ * JSON.parser = JSON::Ext::Parser
+ *
+ * with the method parser= in JSON.
+ *
+ */
+
+static VALUE convert_encoding(VALUE source)
+{
+#ifdef HAVE_RUBY_ENCODING_H
+ rb_encoding *enc = rb_enc_get(source);
+ if (enc == rb_ascii8bit_encoding()) {
+ if (OBJ_FROZEN(source)) {
+ source = rb_str_dup(source);
+ }
+ FORCE_UTF8(source);
+ } else {
+ source = rb_str_conv_enc(source, rb_enc_get(source), rb_utf8_encoding());
+ }
+#endif
+ return source;
+}
+
+/*
+ * call-seq: new(source, opts => {})
+ *
+ * Creates a new JSON::Ext::Parser instance for the string _source_.
+ *
+ * Creates a new JSON::Ext::Parser instance for the string _source_.
+ *
+ * It will be configured by the _opts_ hash. _opts_ can have the following
+ * keys:
+ *
+ * _opts_ can have the following keys:
+ * * *max_nesting*: The maximum depth of nesting allowed in the parsed data
+ * structures. Disable depth checking with :max_nesting => false|nil|0, it
+ * defaults to 100.
+ * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
+ * defiance of RFC 4627 to be parsed by the Parser. This option defaults to
+ * false.
+ * * *symbolize_names*: If set to true, returns symbols for the names
+ * (keys) in a JSON object. Otherwise strings are returned, which is
+ * also the default. It's not possible to use this option in
+ * conjunction with the *create_additions* option.
+ * * *create_additions*: If set to false, the Parser doesn't create
+ * additions even if a matching class and create_id was found. This option
+ * defaults to false.
+ * * *object_class*: Defaults to Hash
+ * * *array_class*: Defaults to Array
+ */
+static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE source, opts;
+ GET_PARSER_INIT;
+
+ if (json->Vsource) {
+ rb_raise(rb_eTypeError, "already initialized instance");
+ }
+#ifdef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
+ rb_scan_args(argc, argv, "1:", &source, &opts);
+#else
+ rb_scan_args(argc, argv, "11", &source, &opts);
+#endif
+ if (!NIL_P(opts)) {
+#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
+ opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
+ if (NIL_P(opts)) {
+ rb_raise(rb_eArgError, "opts needs to be like a hash");
+ } else {
+#endif
+ VALUE tmp = ID2SYM(i_max_nesting);
+ if (option_given_p(opts, tmp)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
+ if (RTEST(max_nesting)) {
+ Check_Type(max_nesting, T_FIXNUM);
+ json->max_nesting = FIX2INT(max_nesting);
+ } else {
+ json->max_nesting = 0;
+ }
+ } else {
+ json->max_nesting = 100;
+ }
+ tmp = ID2SYM(i_allow_nan);
+ if (option_given_p(opts, tmp)) {
+ json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->allow_nan = 0;
+ }
+ tmp = ID2SYM(i_symbolize_names);
+ if (option_given_p(opts, tmp)) {
+ json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->symbolize_names = 0;
+ }
+ tmp = ID2SYM(i_freeze);
+ if (option_given_p(opts, tmp)) {
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
+ } else {
+ json->freeze = 0;
+ }
+ tmp = ID2SYM(i_create_additions);
+ if (option_given_p(opts, tmp)) {
+ json->create_additions = RTEST(rb_hash_aref(opts, tmp));
+ } else {
+ json->create_additions = 0;
+ }
+ if (json->symbolize_names && json->create_additions) {
+ rb_raise(rb_eArgError,
+ "options :symbolize_names and :create_additions cannot be "
+ " used in conjunction");
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ json->create_id = rb_hash_aref(opts, tmp);
+ } else {
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ }
+ tmp = ID2SYM(i_object_class);
+ if (option_given_p(opts, tmp)) {
+ json->object_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->object_class = Qnil;
+ }
+ tmp = ID2SYM(i_array_class);
+ if (option_given_p(opts, tmp)) {
+ json->array_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->array_class = Qnil;
+ }
+ tmp = ID2SYM(i_decimal_class);
+ if (option_given_p(opts, tmp)) {
+ json->decimal_class = rb_hash_aref(opts, tmp);
+ } else {
+ json->decimal_class = Qnil;
+ }
+ tmp = ID2SYM(i_match_string);
+ if (option_given_p(opts, tmp)) {
+ VALUE match_string = rb_hash_aref(opts, tmp);
+ json->match_string = RTEST(match_string) ? match_string : Qnil;
+ } else {
+ json->match_string = Qnil;
+ }
+#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
+ }
+#endif
+ } else {
+ json->max_nesting = 100;
+ json->allow_nan = 0;
+ json->create_additions = 0;
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ json->object_class = Qnil;
+ json->array_class = Qnil;
+ json->decimal_class = Qnil;
+ }
+ source = convert_encoding(StringValue(source));
+ StringValue(source);
+ json->len = RSTRING_LEN(source);
+ json->source = RSTRING_PTR(source);;
+ json->Vsource = source;
+ return self;
+}
+
+%%{
+ machine JSON;
+
+ write data;
+
+ include JSON_common;
+
+ action parse_value {
+ char *np = JSON_parse_value(json, fpc, pe, &result, 0);
+ if (np == NULL) { fhold; fbreak; } else fexec np;
+ }
+
+ main := ignore* (
+ begin_value >parse_value
+ ) ignore*;
+}%%
+
+/*
+ * call-seq: parse()
+ *
+ * Parses the current JSON text _source_ and returns the complete data
+ * structure as a result.
+ */
+static VALUE cParser_parse(VALUE self)
+{
+ char *p, *pe;
+ int cs = EVIL;
+ VALUE result = Qnil;
+ GET_PARSER;
+
+ %% write init;
+ p = json->source;
+ pe = p + json->len;
+ %% write exec;
+
+ if (cs >= JSON_first_final && p == pe) {
+ return result;
+ } else {
+ rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+ return Qnil;
+ }
+}
+
+static void JSON_mark(void *ptr)
+{
+ JSON_Parser *json = ptr;
+ rb_gc_mark_maybe(json->Vsource);
+ rb_gc_mark_maybe(json->create_id);
+ rb_gc_mark_maybe(json->object_class);
+ rb_gc_mark_maybe(json->array_class);
+ rb_gc_mark_maybe(json->decimal_class);
+ rb_gc_mark_maybe(json->match_string);
+}
+
+static void JSON_free(void *ptr)
+{
+ JSON_Parser *json = ptr;
+ fbuffer_free(json->fbuffer);
+ ruby_xfree(json);
+}
+
+static size_t JSON_memsize(const void *ptr)
+{
+ const JSON_Parser *json = ptr;
+ return sizeof(*json) + FBUFFER_CAPA(json->fbuffer);
+}
+
+#ifdef NEW_TYPEDDATA_WRAPPER
+static const rb_data_type_t JSON_Parser_type = {
+ "JSON/Parser",
+ {JSON_mark, JSON_free, JSON_memsize,},
+#ifdef RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0,
+ RUBY_TYPED_FREE_IMMEDIATELY,
+#endif
+};
+#endif
+
+static VALUE cJSON_parser_s_allocate(VALUE klass)
+{
+ JSON_Parser *json;
+ VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
+ json->fbuffer = fbuffer_alloc(0);
+ return obj;
+}
+
+/*
+ * call-seq: source()
+ *
+ * Returns a copy of the current _source_ string, that was used to construct
+ * this Parser.
+ */
+static VALUE cParser_source(VALUE self)
+{
+ GET_PARSER;
+ return rb_str_dup(json->Vsource);
+}
+
+void Init_parser(void)
+{
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
+
+#undef rb_intern
+ rb_require("json/common");
+ mJSON = rb_define_module("JSON");
+ mExt = rb_define_module_under(mJSON, "Ext");
+ cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
+ eParserError = rb_path2class("JSON::ParserError");
+ eNestingError = rb_path2class("JSON::NestingError");
+ rb_gc_register_mark_object(eParserError);
+ rb_gc_register_mark_object(eNestingError);
+ rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
+ rb_define_method(cParser, "initialize", cParser_initialize, -1);
+ rb_define_method(cParser, "parse", cParser_parse, 0);
+ rb_define_method(cParser, "source", cParser_source, 0);
+
+ CNaN = rb_const_get(mJSON, rb_intern("NaN"));
+ rb_gc_register_mark_object(CNaN);
+
+ CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
+ rb_gc_register_mark_object(CInfinity);
+
+ CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
+ rb_gc_register_mark_object(CMinusInfinity);
+
+ i_json_creatable_p = rb_intern("json_creatable?");
+ i_json_create = rb_intern("json_create");
+ i_create_id = rb_intern("create_id");
+ i_create_additions = rb_intern("create_additions");
+ i_chr = rb_intern("chr");
+ i_max_nesting = rb_intern("max_nesting");
+ i_allow_nan = rb_intern("allow_nan");
+ i_symbolize_names = rb_intern("symbolize_names");
+ i_object_class = rb_intern("object_class");
+ i_array_class = rb_intern("array_class");
+ i_decimal_class = rb_intern("decimal_class");
+ i_match = rb_intern("match");
+ i_match_string = rb_intern("match_string");
+ i_key_p = rb_intern("key?");
+ i_deep_const_get = rb_intern("deep_const_get");
+ i_aset = rb_intern("[]=");
+ i_aref = rb_intern("[]");
+ i_leftshift = rb_intern("<<");
+ i_new = rb_intern("new");
+ i_try_convert = rb_intern("try_convert");
+ i_freeze = rb_intern("freeze");
+ i_uminus = rb_intern("-@");
+}
+
+/*
+ * Local variables:
+ * mode: c
+ * c-file-style: ruby
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/json/parser/prereq.mk b/ext/json/parser/prereq.mk
new file mode 100644
index 0000000000..fc59169056
--- /dev/null
+++ b/ext/json/parser/prereq.mk
@@ -0,0 +1,13 @@
+RAGEL = ragel
+
+.SUFFIXES: .rl
+
+.rl.c:
+ $(RAGEL) -G2 $<
+ $(BASERUBY) -pli -e '$$_.sub!(/[ \t]+$$/, "")' \
+ -e '$$_.sub!(/^static const int (JSON_.*=.*);$$/, "enum {\\1};")' \
+ -e '$$_.sub!(/^(static const char) (_JSON(?:_\w+)?_nfa_\w+)(?=\[\] =)/, "\\1 MAYBE_UNUSED(\\2)")' \
+ -e '$$_.sub!(/0 <= ([\( ]+\*[\( ]*p\)+) && \1 <= 31/, "0 <= (signed char)(*(p)) && (*(p)) <= 31")' \
+ -e '$$_ = "/* This file is automatically generated from parser.rl by using ragel */\n" + $$_ if $$. == 1' $@
+
+parser.c: