Commits

Genki Sugawara committed 0ea939b

update

Comments (0)

Files changed (16)

generator/generator.c

+#include "../fbuffer/fbuffer.h"
 #include "generator.h"
 
 #ifdef HAVE_RUBY_ENCODING_H
 
 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_dup;
+          i_quirks_mode, 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;
 
 /*
  * 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
  * 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
  * 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, 
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
     0x03C82080UL, 0xFA082080UL, 0x82082080UL };
 
 /*
 }
 
 /* Escapes the UTF16 character and stores the result in the buffer buf, then
- * the buffer buf іs appended to the FBuffer buffer. */
+ * the buffer buf is appended to the FBuffer buffer. */
 static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
         character)
 {
   return result;
 }
 
-/* fbuffer implementation */
-
-static FBuffer *fbuffer_alloc()
-{
-    FBuffer *fb = ALLOC(FBuffer);
-    memset((void *) fb, 0, sizeof(FBuffer));
-    fb->initial_length = FBUFFER_INITIAL_LENGTH;
-    return fb;
-}
-
-static FBuffer *fbuffer_alloc_with_length(unsigned long initial_length)
-{
-    FBuffer *fb;
-    assert(initial_length > 0);
-    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_free_only_buffer(FBuffer *fb)
-{
-    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;
-    }
-}
-
-static void fbuffer_append_char(FBuffer *fb, char newchr)
-{
-    fbuffer_inc_capa(fb, 1);
-    *(fb->ptr + fb->len) = newchr;
-    fb->len++;
-}
-
-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;
-
-    if (len > 0) {
-        result = fbuffer_alloc_with_length(len);
-        fbuffer_append(result, FBUFFER_PAIR(fb));
-    } else {
-        result = fbuffer_alloc();
-    }
-    return result;
-}
-
-/* 
+/*
  * Document-module: JSON::Ext::Generator
  *
  * This is the JSON generator implemented as a C extension. It can be
             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_quirks_mode));
+    state->quirks_mode = RTEST(tmp);
     return self;
 }
 
     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_quirks_mode), state->quirks_mode ? Qtrue : Qfalse);
     rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
     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;
 }
 
 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(buffer, RSTRING_PAIR(tmp));
+    fbuffer_append_str(buffer, tmp);
 }
 
 static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
             rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
         }
     }
-    fbuffer_append(buffer, RSTRING_PAIR(tmp));
+    fbuffer_append_str(buffer, tmp);
 }
 
 static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE 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(buffer, RSTRING_PAIR(tmp));
+        fbuffer_append_str(buffer, tmp);
     } else {
         tmp = rb_funcall(obj, i_to_s, 0);
         Check_Type(tmp, T_STRING);
 
 static FBuffer *cState_prepare_buffer(VALUE self)
 {
-    FBuffer *buffer = fbuffer_alloc();
+    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_with_length(16);
+        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_with_length(16);
+        state->object_delim2 = fbuffer_alloc(16);
     }
     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_with_length(16);
+        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 fbuffer_to_s(FBuffer *fb)
-{
-    VALUE result = rb_str_new(FBUFFER_PAIR(fb));
-    fbuffer_free(fb);
-    FORCE_UTF8(result);
-    return result;
-}
-
 static VALUE cState_partial_generate(VALUE self, VALUE obj)
 {
     FBuffer *buffer = cState_prepare_buffer(self);
 }
 
 /*
+ * This function returns true if string is either a JSON array or JSON object.
+ * It might suffer from false positives, e. g. syntactically incorrect JSON in
+ * the string or certain UTF-8 characters on the right hand side.
+ */
+static int isArrayOrObject(VALUE string)
+{
+    long string_len = RSTRING_LEN(string);
+    char *p = RSTRING_PTR(string), *q = p + string_len - 1;
+    if (string_len < 2) return 0;
+    for (; p < q && isspace(*p); p++);
+    for (; q > p && isspace(*q); q--);
+    return (*p == '[' && *q == ']') || (*p == '{' && *q == '}');
+}
+
+/*
  * call-seq: generate(obj)
  *
  * Generates a valid JSON document from object +obj+ and returns the
 static VALUE cState_generate(VALUE self, VALUE obj)
 {
     VALUE result = cState_partial_generate(self, obj);
-    VALUE re, args[2];
-    args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z");
-    args[1] = CRegexp_MULTILINE;
-    re = rb_class_new_instance(2, args, rb_cRegexp);
-    if (NIL_P(rb_funcall(re, i_match, 1, result))) {
+    GET_STATE(self);
+    if (!state->quirks_mode && !isArrayOrObject(result)) {
         rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed");
     }
     return result;
  * * *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: ''), 
+ * * *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.
+ * * *quirks_mode*: Enables quirks_mode for parser, that is for example
+ *   generating single JSON values instead of documents is possible.
+ * * *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 = 19;
+    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: quirks_mode?
+ *
+ * Returns true, if quirks mode is enabled. Otherwise returns false.
+ */
+static VALUE cState_quirks_mode_p(VALUE self)
+{
+    GET_STATE(self);
+    return state->quirks_mode ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: quirks_mode=(enable)
+ *
+ * If set to true, enables the quirks_mode mode.
+ */
+static VALUE cState_quirks_mode_set(VALUE self, VALUE enable)
+{
+    GET_STATE(self);
+    state->quirks_mode = RTEST(enable);
+    return Qnil;
+}
+
+/*
  * call-seq: depth
  *
  * This integer returns the current depth of data structure nesting.
 {
     GET_STATE(self);
     Check_Type(depth, T_FIXNUM);
-    return state->depth = FIX2LONG(depth);
+    state->depth = FIX2LONG(depth);
+    return Qnil;
+}
+
+/*
+ * call-seq: buffer_initial_length
+ *
+ * This integer returns the current inital 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;
 }
 
 /*
  *
  */
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
 void Init_generator()
 {
     rb_require("json/common");
     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, "quirks_mode?", cState_quirks_mode_p, 0);
+    rb_define_method(cState, "quirks_mode", cState_quirks_mode_p, 0);
+    rb_define_method(cState, "quirks_mode=", cState_quirks_mode_set, 1);
     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);
     i_max_nesting = rb_intern("max_nesting");
     i_allow_nan = rb_intern("allow_nan");
     i_ascii_only = rb_intern("ascii_only");
+    i_quirks_mode = rb_intern("quirks_mode");
     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");

generator/generator.h

 #include <string.h>
 #include <assert.h>
 #include <math.h>
+#include <ctype.h>
 
 #include "ruby.h"
 
-#if HAVE_RUBY_RE_H
+#ifdef HAVE_RUBY_RE_H
 #include "ruby/re.h"
-#endif
-
-#if HAVE_RE_H
+#else
 #include "re.h"
 #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
-
 #define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
 
-#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_PTR
-#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
-#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
-
-#define RSTRING_PAIR(string) RSTRING_PTR(string), RSTRING_LEN(string)
-
-/* fbuffer implementation */
-
-typedef struct FBufferStruct {
-    unsigned long initial_length;
-    char *ptr;
-    unsigned long len;
-    unsigned long capa;
-} FBuffer;
-
-#define FBUFFER_INITIAL_LENGTH 4096
-
-#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 char *fstrndup(const char *ptr, unsigned long len);
-static FBuffer *fbuffer_alloc();
-static FBuffer *fbuffer_alloc_with_length(unsigned long initial_length);
-static void fbuffer_free(FBuffer *fb);
-static void fbuffer_free_only_buffer(FBuffer *fb);
-static void fbuffer_clear(FBuffer *fb);
-static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
-static void fbuffer_append_long(FBuffer *fb, long number);
-static void fbuffer_append_char(FBuffer *fb, char newchr);
-static FBuffer *fbuffer_dup(FBuffer *fb);
-static VALUE fbuffer_to_s(FBuffer *fb);
-
 /* unicode defintions */
 
 #define UNI_STRICT_CONVERSION 1
 static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character);
 static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string);
 static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string);
+static char *fstrndup(const char *ptr, unsigned long len);
 
 /* ruby api and some helpers */
 
     long max_nesting;
     char allow_nan;
     char ascii_only;
+    char quirks_mode;
     long depth;
+    long buffer_initial_length;
 } JSON_Generator_State;
 
 #define GET_STATE(self)                       \

generator/generator.vcxproj

       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GENERATOR_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GENERATOR_EXPORTS;JSON_GENERATOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>C:\usr\local\ruby-1.8\lib\ruby\1.8\i386-mswin32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
     </ClCompile>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;GENERATOR_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;GENERATOR_EXPORTS;JSON_GENERATOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>C:\usr\local\ruby-1.8\lib\ruby\1.8\i386-mswin32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
     </ClCompile>
       <AdditionalDependencies>msvcrt-ruby18.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     <PostBuildEvent>
-      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)lib/json/ext/1.8/"</Command>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "$(SolutionDir)lib/json/ext/"</Command>
     </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemGroup>
 
 Gem::Specification.new do |s|
   s.name = %q{json}
-  s.version = "1.5.3"
+  s.version = "1.7.5"
   s.platform = 'mswin32'
 
   s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
   s.date = %q{2011-06-20}
   s.description = %q{This is a JSON implementation as a Ruby extension in C.}
   s.email = %q{flori@ping.de}
-  s.executables = [%q{edit_json.rb}, %q{prettify_json.rb}]
-  s.files = Dir.glob('bin/**/*')+  Dir.glob('lib/**/*')
+  s.files = Dir.glob('lib/**/*')
   s.homepage = %q{http://flori.github.com/json}
   s.require_paths = [%q{lib}]
   s.rubyforge_project = %q{json}
 #
 # Built on two universally available structures:
 #   1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array.
-#   2. An orderd list of values. More commonly named as an _array_, vector, sequence, or list.
+#   2. An ordered list of values. More commonly called an _array_, vector, sequence or list.
 #
 # To read more about JSON visit: http://json.org
 #
 # == Parsing JSON
 #
-# To parse a JSON string received by another application, or generated within
+# To parse a JSON string received by another application or generated within
 # your existing application:
-#   
+#
 #   require 'json'
 #
 #   my_hash = JSON.parse('{"hello": "goodbye"}')
 # just as simple.
 #
 #   require 'json'
-#   
+#
 #   my_hash = {:hello => "goodbye"}
 #   puts JSON.generate(my_hash) => "{\"hello\":\"goodbye\"}"
 #
 #   require 'json'
 #   puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}"
 #
-# <tt>JSON.generate</tt> only allows objects or arrays to be converted 
-# to JSON syntax. While <tt>to_json</tt> accepts many Ruby classes 
-# even though it only acts a method for serialization:
+# <tt>JSON.generate</tt> only allows objects or arrays to be converted
+# to JSON syntax. <tt>to_json</tt>, however, accepts many Ruby classes
+# even though it acts only as a method for serialization:
 #
 #   require 'json'
 #
-# This file contains implementations of ruby core's custom objects for
+# This file requires the implementations of ruby core's custom objects for
 # serialisation/deserialisation.
 
-unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
-  require 'json'
-end
-require 'date'
-
-# Symbol serialization/deserialization
-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
-
-# Time serialization/deserialization
-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 respond_to?(:tv_nsec)
-      at(*object.values_at('s', 'n'))
-    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(*)
-    {
-      JSON.create_id => self.class.name,
-      's'            => tv_sec,
-      'n'            => respond_to?(:tv_nsec) ? tv_nsec : tv_usec * 1000
-    }
-  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
-
-# Date serialization/deserialization
-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
-
-# DateTime serialization/deserialization
-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
-
-# Range serialization/deserialization
-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
-
-# Struct serialization/deserialization
-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
-
-# Exception serialization/deserialization
-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
-
-# Regexp serialization/deserialization
-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(*)
-    as_json.to_json
-  end
-end
+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'
 require 'json/version'
+require 'json/generic_object'
 
 module JSON
   class << self
-    # If _object_ is string-like parse the string and return the parsed result
+    # 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
+    # The _opts_ argument is passed through to generate/parse respectively. See
     # generate and parse for their documentation.
     def [](object, opts = {})
       if object.respond_to? :to_str
       end
     end
 
-    # Returns the JSON parser class, that is used by JSON. This might be either
+    # Returns the JSON parser class that is used by JSON. This is either
     # JSON::Ext::Parser or JSON::Pure::Parser.
     attr_reader :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
+    # 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:
       $VERBOSE = old
     end
 
-    # Returns the JSON generator modul, that is used by JSON. This might be
+    # Returns the JSON generator module that is used by JSON. This is
     # either JSON::Ext::Generator or JSON::Pure::Generator.
     attr_reader :generator
 
-    # Returns the JSON generator state class, that is used by JSON. This might
-    # be either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
+    # Returns the JSON generator state class that is used by JSON. This is
+    # either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
     attr_accessor :state
 
-    # This is create identifier, that is used to decide, if the _json_create_
+    # This is create identifier, which is used to decide if the _json_create_
     # hook of a class should be called. It defaults to 'json_class'.
     attr_accessor :create_id
   end
   MinusInfinity = -Infinity
 
   # The base exception for JSON errors.
-  class JSONError < StandardError; end
+  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.
+  # This exception is raised if a parser error occurs.
   class ParserError < JSONError; end
 
-  # This exception is raised, if the nesting of parsed datastructures is too
+  # This exception is raised if the nesting of parsed data structures is too
   # deep.
   class NestingError < ParserError; end
 
   class CircularDatastructure < NestingError; end
   # :startdoc:
 
-  # This exception is raised, if a generator or unparser error occurs.
+  # This exception is raised if a generator or unparser error occurs.
   class GeneratorError < JSONError; end
   # For backwards compatibility
   UnparserError = GeneratorError
 
-  # This exception is raised, if the required unicode support is missing on the
-  # system. Usually this means, that the iconv library is not installed.
+  # 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
   # _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, it defaults
+  #   structures. Disable depth checking with :max_nesting => false. It defaults
   #   to 19.
   # * *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
+  #   (keys) in a JSON object. Otherwise strings are returned. Strings are
   #   the default.
   # * *create_additions*: If set to false, the Parser doesn't create
-  #   additions even if a matchin class and create_id was found. This option
+  #   additions even if a matching class and create_id was found. This option
   #   defaults to true.
   # * *object_class*: Defaults to Hash
   # * *array_class*: Defaults to Array
   end
 
   # Parse the JSON document _source_ into a Ruby data structure and return it.
-  # The bang version of the parse method, defaults to the more dangerous values
+  # The bang version of the parse method defaults to the more dangerous values
   # for the _opts_ hash, so be sure only to parse trusted _source_ documents.
   #
   # _opts_ can have the following keys:
   # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
   #   structures. Enable depth checking with :max_nesting => anInteger. The parse!
-  #   methods defaults to not doing max depth checking: This can be dangerous,
+  #   methods defaults to not doing max depth checking: This can be dangerous
   #   if someone wants to fill up your stack.
   # * *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 true.
   # * *create_additions*: If set to false, the Parser doesn't create
-  #   additions even if a matchin class and create_id was found. This option
+  #   additions even if a matching class and create_id was found. This option
   #   defaults to true.
   def parse!(source, opts = {})
     opts = {
   # * *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: ''), 
+  # * *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
+  #   generated, otherwise an exception is thrown if these values are
   #   encountered. This options defaults to false.
   # * *max_nesting*: The maximum depth of nesting allowed in the data
   #   structures from which JSON is to be generated. Disable depth checking
   #
   # See also the fast_generate for the fastest creation method with the least
   # amount of sanity checks, and the pretty_generate method for some
-  # defaults for a pretty output.
+  # defaults for pretty output.
   def generate(obj, opts = nil)
-    state = SAFE_STATE_PROTOTYPE.dup
+    if State === opts
+      state, opts = opts, nil
+    else
+      state = SAFE_STATE_PROTOTYPE.dup
+    end
     if opts
       if opts.respond_to? :to_hash
         opts = opts.to_hash
   # This method disables the checks for circles in Ruby objects.
   #
   # *WARNING*: Be careful not to pass any Ruby data structures with circles as
-  # _obj_ argument, because this will cause JSON to go into an infinite loop.
+  # _obj_ argument because this will cause JSON to go into an infinite loop.
   def fast_generate(obj, opts = nil)
-    state = FAST_STATE_PROTOTYPE.dup
+    if State === opts
+      state, opts = opts, nil
+    else
+      state = FAST_STATE_PROTOTYPE.dup
+    end
     if opts
       if opts.respond_to? :to_hash
         opts = opts.to_hash
   # The returned document is a prettier form of the document returned by
   # #unparse.
   #
-  # The _opts_ argument can be used to configure the generator, see the
+  # The _opts_ argument can be used to configure the generator. See the
   # generate method for a more detailed explanation.
   def pretty_generate(obj, opts = nil)
-    state = PRETTY_STATE_PROTOTYPE.dup
+    if State === opts
+      state, opts = opts, nil
+    else
+      state = PRETTY_STATE_PROTOTYPE.dup
+    end
     if opts
       if opts.respond_to? :to_hash
         opts = opts.to_hash
   module_function :pretty_unparse
   # :startdoc:
 
+  class << self
+    # The global default options for the JSON.load method:
+    #  :max_nesting: false
+    #  :allow_nan:   true
+    #  :quirks_mode: true
+    attr_accessor :load_default_options
+  end
+  self.load_default_options = {
+    :max_nesting => false,
+    :allow_nan   => true,
+    :quirks_mode => true,
+  }
+
   # Load a ruby data structure from a JSON _source_ and return it. A source can
-  # either be a string-like object, an IO like object, or an object responding
+  # either be a string-like object, an IO-like object, or an object responding
   # to the read method. If _proc_ was given, it will be called with any nested
-  # Ruby object as an argument recursively in depth first order.
+  # Ruby object as an argument recursively in depth first order. The default
+  # options for the parser can be changed via the load_default_options method.
   #
   # This method is part of the implementation of the load/dump interface of
   # Marshal and YAML.
   def load(source, proc = nil)
+    opts = load_default_options
     if source.respond_to? :to_str
       source = source.to_str
     elsif source.respond_to? :to_io
       source = source.to_io.read
-    else
+    elsif source.respond_to?(:read)
       source = source.read
     end
-    result = parse(source, :max_nesting => false, :allow_nan => true)
+    if opts[:quirks_mode] && (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)
     case result
   alias restore load
   module_function :restore
 
+  class << self
+    # The global default options for the JSON.dump method:
+    #  :max_nesting: false
+    #  :allow_nan:   true
+    #  :quirks_mode: true
+    attr_accessor :dump_default_options
+  end
+  self.dump_default_options = {
+    :max_nesting => false,
+    :allow_nan   => true,
+    :quirks_mode => true,
+  }
+
   # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
   # the result.
   #
-  # If anIO (an IO like object or an object that responds to the write method)
+  # If anIO (an IO-like object or an object that responds to the write method)
   # was given, the resulting JSON is written to it.
   #
-  # If the number of nested arrays or objects exceeds _limit_ an ArgumentError
+  # If the number of nested arrays or objects exceeds _limit_, an ArgumentError
   # exception is raised. This argument is similar (but not exactly the
   # same!) to the _limit_ argument in Marshal.dump.
   #
+  # The default options for the generator can be changed via the
+  # dump_default_options method.
+  #
   # This method is part of the implementation of the load/dump interface of
   # Marshal and YAML.
   def dump(obj, anIO = nil, limit = nil)
         anIO = nil
       end
     end
-    limit ||= 0
-    result = generate(obj, :allow_nan => true, :max_nesting => limit)
+    opts = JSON.dump_default_options
+    limit and opts.update(:max_nesting => limit)
+    result = generate(obj, opts)
     if anIO
       anIO.write result
       anIO
   end
 
   # Shortuct for iconv.
-  if ::String.method_defined?(:encode)
+  if ::String.method_defined?(:encode) &&
+    # XXX Rubinius doesn't support ruby 1.9 encoding yet
+    defined?(RUBY_ENGINE) && RUBY_ENGINE != 'rbx'
+  then
     # Encodes string using Ruby's _String.encode_
     def self.iconv(to, from, string)
       string.encode(to, from)
     require 'iconv'
     # Encodes string using _iconv_ library
     def self.iconv(to, from, string)
-      Iconv.iconv(to, from, string).first
+      Iconv.conv(to, from, string)
     end
   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
+  # 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
+  # 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
 
 # Extends any Class to include _json_creatable?_ method.
 class ::Class
-  # Returns true, if this class can be used to create an instance
+  # 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, which includes
-  # the required data.
+  # 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
+if ENV['SIMPLECOV_COVERAGE'].to_i == 1
+  require 'simplecov'
+  SimpleCov.start do
+    add_filter "/tests/"
+  end
+end
 require 'json/common'
 
 module JSON
   # This module holds all the modules/classes that implement JSON's
   # functionality as C extensions.
   module Ext
-    begin
-      if defined?(RUBY_ENGINE) == 'constant' and RUBY_ENGINE == 'ruby' and RUBY_VERSION =~ /\A1\.9\./
-        require 'json/ext/1.9/parser'
-        require 'json/ext/1.9/generator'
-      elsif !defined?(RUBY_ENGINE) && RUBY_VERSION =~ /\A1\.8\./
-        require 'json/ext/1.8/parser'
-        require 'json/ext/1.8/generator'
-      else
-        require 'json/ext/parser'
-        require 'json/ext/generator'
-      end
-    rescue LoadError
-      require 'json/ext/parser'
-      require 'json/ext/generator'
-    end
+    require 'json/ext/parser'
+    require 'json/ext/generator'
     $DEBUG and warn "Using Ext extension for JSON."
     JSON.parser = Parser
     JSON.generator = Generator
+if ENV['SIMPLECOV_COVERAGE'].to_i == 1
+  require 'simplecov'
+  SimpleCov.start do
+    add_filter "/tests/"
+  end
+end
 require 'json/common'
 require 'json/pure/parser'
 require 'json/pure/generator'

lib/json/pure/generator.rb

   if defined?(::Encoding)
     def utf8_to_json(string) # :nodoc:
       string = string.dup
-      string << '' # XXX workaround: avoid buffer sharing
       string.force_encoding(::Encoding::ASCII_8BIT)
       string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
       string.force_encoding(::Encoding::UTF_8)
 
     def utf8_to_json_ascii(string) # :nodoc:
       string = string.dup
-      string << '' # XXX workaround: avoid buffer sharing
       string.force_encoding(::Encoding::ASCII_8BIT)
-      string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
+      string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
       string.gsub!(/(
                       (?:
                         [\xc2-\xdf][\x80-\xbf]    |
                     )/nx) { |c|
                       c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
                       s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
+                      s.force_encoding(::Encoding::ASCII_8BIT)
                       s.gsub!(/.{4}/n, '\\\\u\&')
+                      s.force_encoding(::Encoding::UTF_8)
                     }
       string.force_encoding(::Encoding::UTF_8)
       string
     rescue => e
-      raise GeneratorError, "Caught #{e.class}: #{e}"
+      raise GeneratorError.wrap(e)
     end
   else
     def utf8_to_json(string) # :nodoc:
-      string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
+      string.gsub(/["\\\x0-\x1f]/n) { MAP[$&] }
     end
 
     def utf8_to_json_ascii(string) # :nodoc:
       }
       string
     rescue => e
-      raise GeneratorError, "Caught #{e.class}: #{e}"
+      raise GeneratorError.wrap(e)
     end
   end
   module_function :utf8_to_json, :utf8_to_json_ascii
         # * *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: ''), 
+        # * *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: ''),
         # * *check_circular*: is deprecated now, use the :max_nesting option instead,
         # * *max_nesting*: sets the maximum level of data structure nesting in
         # * *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.
+        # * *quirks_mode*: Enables quirks_mode for parser, that is for example
+        #   generating single JSON values instead of documents is possible.
         def initialize(opts = {})
-          @indent         = ''
-          @space          = ''
-          @space_before   = ''
-          @object_nl      = ''
-          @array_nl       = ''
-          @allow_nan      = false
-          @ascii_only     = false
+          @indent                = ''
+          @space                 = ''
+          @space_before          = ''
+          @object_nl             = ''
+          @array_nl              = ''
+          @allow_nan             = false
+          @ascii_only            = false
+          @quirks_mode           = false
+          @buffer_initial_length = 1024
           configure opts
         end
 
         # the generated JSON, max_nesting = 0 if no maximum is checked.
         attr_accessor :max_nesting
 
+        # If this attribute is set to true, quirks mode is enabled, otherwise
+        # it's disabled.
+        attr_accessor :quirks_mode
+
+        # :stopdoc:
+        attr_reader :buffer_initial_length
+
+        def buffer_initial_length=(length)
+          if length > 0
+            @buffer_initial_length = length
+          end
+        end
+        # :startdoc:
+
         # This integer returns the current depth data structure nesting in the
         # generated JSON.
         attr_accessor :depth
           @allow_nan
         end
 
+        # Returns true, if only ASCII characters should be generated. Otherwise
+        # returns false.
         def ascii_only?
           @ascii_only
         end
 
+        # Returns true, if quirks mode is enabled. Otherwise returns false.
+        def quirks_mode?
+          @quirks_mode
+        end
+
         # Configure this State instance with the Hash _opts_, and return
         # itself.
         def configure(opts)
           @allow_nan      = !!opts[:allow_nan] if opts.key?(:allow_nan)
           @ascii_only     = opts[:ascii_only] if opts.key?(:ascii_only)
           @depth          = opts[:depth] || 0
+          @quirks_mode    = opts[:quirks_mode] if opts.key?(:quirks_mode)
           if !opts.key?(:max_nesting) # defaults to 19
             @max_nesting = 19
           elsif opts[:max_nesting]
         # passed to the configure method.
         def to_h
           result = {}
-          for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth]
+          for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only quirks_mode buffer_initial_length depth]
             result[iv.intern] = instance_variable_get("@#{iv}")
           end
           result
         # GeneratorError exception.
         def generate(obj)
           result = obj.to_json(self)
-          if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
-            raise GeneratorError, "only generation of JSON objects or arrays allowed"
+          unless @quirks_mode
+            unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ ||
+              result =~ /\A\s*\{/ && result =~ /\}\s*\Z/
+            then
+              raise GeneratorError, "only generation of JSON objects or arrays allowed"
+            end
           end
           result
         end

lib/json/pure/parser.rb

           [^*/]|        # normal chars
           /[^*]|        # slashes that do not start a nested comment
           \*[^/]|       # asterisks that do not end this comment
-          /(?=\*/)      # single slash before this comment's end 
+          /(?=\*/)      # single slash before this comment's end
          )*
            \*/               # the End of this comment
            |[ \t\r\n]+       # whitespaces: space, horicontal tab, lf, cr
       #   defaults to true.
       # * *object_class*: Defaults to Hash
       # * *array_class*: Defaults to Array
+      # * *quirks_mode*: Enables quirks_mode for parser, that is for example
+      #   parsing single JSON values instead of documents is possible.
       def initialize(source, opts = {})
         opts ||= {}
-        if defined?(::Encoding)
-          if source.encoding == ::Encoding::ASCII_8BIT
-            b = source[0, 4].bytes.to_a
-            source = case
-                     when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
-                       source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
-                     when b.size >= 4 && b[0] == 0 && b[2] == 0
-                       source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
-                     when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
-                       source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
-
-                     when b.size >= 4 && b[1] == 0 && b[3] == 0
-                       source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
-                     else
-                       source.dup
-                     end
-          else
-            source = source.encode(::Encoding::UTF_8)
-          end
-          source.force_encoding(::Encoding::ASCII_8BIT)
-        else
-          b = source
-          source = case
-                   when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
-                     JSON.iconv('utf-8', 'utf-32be', b)
-                   when b.size >= 4 && b[0] == 0 && b[2] == 0
-                     JSON.iconv('utf-8', 'utf-16be', b)
-                   when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
-                     JSON.iconv('utf-8', 'utf-32le', b)
-                   when b.size >= 4 && b[1] == 0 && b[3] == 0
-                     JSON.iconv('utf-8', 'utf-16le', b)
-                   else
-                     b
-                   end
+        unless @quirks_mode = opts[:quirks_mode]
+          source = convert_encoding source
         end
         super source
         if !opts.key?(:max_nesting) # defaults to 19
         else
           @max_nesting = 0
         end
-        @allow_nan        = !!opts[:allow_nan]
-        @symbolize_names  = !!opts[:symbolize_names]
-        @create_additions = opts.key?(:create_additions) ? !!opts[:create_additions] : true
-        @create_id        = opts[:create_id] || JSON.create_id
-        @object_class     = opts[:object_class] || Hash
-        @array_class      = opts[:array_class] || Array
-        @match_string     = opts[:match_string]
+        @allow_nan = !!opts[:allow_nan]
+        @symbolize_names = !!opts[:symbolize_names]
+        if opts.key?(:create_additions)
+          @create_additions = !!opts[:create_additions]
+        else
+          @create_additions = true
+        end
+        @create_id = @create_additions ? JSON.create_id : nil
+        @object_class = opts[:object_class] || Hash
+        @array_class  = opts[:array_class] || Array
+        @match_string = opts[:match_string]
       end
 
       alias source string
 
+      def quirks_mode?
+        !!@quirks_mode
+      end
+
+      def reset
+        super
+        @current_nesting = 0
+      end
+
       # Parses the current JSON string _source_ and returns the complete data
       # structure as a result.
       def parse
         reset
         obj = nil
-        until eos?
-          case
-          when scan(OBJECT_OPEN)
-            obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
-            @current_nesting = 1
-            obj = parse_object
-          when scan(ARRAY_OPEN)
-            obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
-            @current_nesting = 1
-            obj = parse_array
-          when skip(IGNORE)
-            ;
+        if @quirks_mode
+          while !eos? && skip(IGNORE)
+          end
+          if eos?
+            raise ParserError, "source did not contain any JSON!"
           else
-            raise ParserError, "source '#{peek(20)}' not in JSON!"
+            obj = parse_value
+            obj == UNPARSED and raise ParserError, "source did not contain any JSON!"
           end
+        else
+          until eos?
+            case
+            when scan(OBJECT_OPEN)
+              obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
+              @current_nesting = 1
+              obj = parse_object
+            when scan(ARRAY_OPEN)
+              obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
+              @current_nesting = 1
+              obj = parse_array
+            when skip(IGNORE)
+              ;
+            else
+              raise ParserError, "source '#{peek(20)}' not in JSON!"
+            end
+          end
+          obj or raise ParserError, "source did not contain any JSON!"
         end
-        obj or raise ParserError, "source did not contain any JSON!"
         obj
       end
 
       private
 
+      def convert_encoding(source)
+        if source.respond_to?(:to_str)
+          source = source.to_str
+        else
+          raise TypeError, "#{source.inspect} is not like a string"
+        end
+        if defined?(::Encoding)
+          if source.encoding == ::Encoding::ASCII_8BIT
+            b = source[0, 4].bytes.to_a
+            source =
+              case
+              when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
+                source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
+              when b.size >= 4 && b[0] == 0 && b[2] == 0
+                source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
+              when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
+                source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
+              when b.size >= 4 && b[1] == 0 && b[3] == 0
+                source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
+              else
+                source.dup
+              end
+          else
+            source = source.encode(::Encoding::UTF_8)
+          end
+          source.force_encoding(::Encoding::ASCII_8BIT)
+        else
+          b = source
+          source =
+            case
+            when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
+              JSON.iconv('utf-8', 'utf-32be', b)
+            when b.size >= 4 && b[0] == 0 && b[2] == 0
+              JSON.iconv('utf-8', 'utf-16be', b)
+            when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
+              JSON.iconv('utf-8', 'utf-32le', b)
+            when b.size >= 4 && b[1] == 0 && b[3] == 0
+              JSON.iconv('utf-8', 'utf-16le', b)
+            else
+              b
+            end
+        end
+        source
+      end
+
       # Unescape characters in strings.
       UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
       UNESCAPE_MAP.update({
         ?n  => "\n",
         ?r  => "\r",
         ?t  => "\t",
-        ?u  => nil, 
+        ?u  => nil,
       })
 
       EMPTY_8BIT_STRING = ''
       if ::String.method_defined?(:encode)
-        EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT 
+        EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
       end
 
       def parse_string
 module JSON
   # JSON version
-  VERSION         = '1.5.3'
+  VERSION         = '1.7.5'
   VERSION_ARRAY   = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
   VERSION_MAJOR   = VERSION_ARRAY[0] # :nodoc:
   VERSION_MINOR   = VERSION_ARRAY[1] # :nodoc:
 
 #line 1 "parser.rl"
+#include "../fbuffer/fbuffer.h"
 #include "parser.h"
 
 /* unicode */
 
-static const char digit_values[256] = { 
+static const 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,
     return result;
 }
 
-static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) 
+static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
 {
     int len = 1;
     if (ch <= 0x7F) {
 #ifdef HAVE_RUBY_ENCODING_H
 static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8, CEncoding_UTF_16BE,
     CEncoding_UTF_16LE, CEncoding_UTF_32BE, CEncoding_UTF_32LE;
-static ID i_encoding, i_encode, i_encode_bang, i_force_encoding;
+static ID i_encoding, i_encode;
 #else
 static ID i_iconv;
 #endif
 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_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_leftshift;
+          i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_quirks_mode,
+          i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match,
+          i_match_string, i_aset, i_aref, i_leftshift;
 
 
-#line 108 "parser.rl"
+#line 110 "parser.rl"
 
 
 
-#line 90 "parser.c"
+#line 92 "parser.c"
 static const int JSON_object_start = 1;
 static const int JSON_object_first_final = 27;
 static const int JSON_object_error = 0;
 static const int JSON_object_en_main = 1;
 
 
-#line 148 "parser.rl"
+#line 151 "parser.rl"
 
 
 static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
 
     *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
 
-    
-#line 114 "parser.c"
+
+#line 116 "parser.c"
 	{
 	cs = JSON_object_start;
 	}
 
-#line 163 "parser.rl"
-    
-#line 121 "parser.c"
+#line 166 "parser.rl"
+
+#line 123 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
 		goto st2;
 	goto st0;
 tr2:
-#line 131 "parser.rl"
+#line 133 "parser.rl"
 	{
         char *np;
         json->parsing_name = 1;
 	if ( ++p == pe )
 		goto _test_eof3;
 case 3:
-#line 162 "parser.c"
+#line 164 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st3;
 		case 32: goto st3;
 		goto st8;
 	goto st0;
 tr11:
-#line 116 "parser.rl"
+#line 118 "parser.rl"
 	{
         VALUE v = Qnil;
-        char *np = JSON_parse_value(json, p, pe, &v); 
+        char *np = JSON_parse_value(json, p, pe, &v);
         if (np == NULL) {
             p--; {p++; cs = 9; goto _out;}
         } else {
             if (NIL_P(json->object_class)) {
-              rb_hash_aset(*result, last_name, v);
+                rb_hash_aset(*result, last_name, v);
             } else {
-              rb_funcall(*result, i_aset, 2, last_name, v);
+                rb_funcall(*result, i_aset, 2, last_name, v);
             }
             {p = (( np))-1;}
         }
 	if ( ++p == pe )
 		goto _test_eof9;
 case 9:
-#line 249 "parser.c"
+#line 251 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st9;
 		case 32: goto st9;
 		goto st9;
 	goto st18;
 tr4:
-#line 139 "parser.rl"
+#line 141 "parser.rl"
 	{ p--; {p++; cs = 27; goto _out;} }
 	goto st27;
 st27:
 	if ( ++p == pe )
 		goto _test_eof27;
 case 27:
-#line 345 "parser.c"
+#line 347 "parser.c"
 	goto st0;
 st19:
 	if ( ++p == pe )
 		goto st2;
 	goto st26;
 	}
-	_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_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 164 "parser.rl"
+#line 167 "parser.rl"
 
     if (cs >= JSON_object_first_final) {
         if (json->create_additions) {
-            VALUE klassname = rb_hash_aref(*result, json->create_id);
+            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))) {
 }
 
 
-#line 462 "parser.c"
+
+#line 470 "parser.c"
 static const int JSON_value_start = 1;
 static const int JSON_value_first_final = 21;
 static const int JSON_value_error = 0;
 static const int JSON_value_en_main = 1;
 
 
-#line 262 "parser.rl"
+#line 271 "parser.rl"
 
 
 static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
 {
     int cs = EVIL;
 
-    
-#line 478 "parser.c"
+
+#line 486 "parser.c"
 	{
 	cs = JSON_value_start;
 	}
 
-#line 269 "parser.rl"
-    
-#line 485 "parser.c"
+#line 278 "parser.rl"
+
+#line 493 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
 cs = 0;
 	goto _out;
 tr0:
-#line 210 "parser.rl"
+#line 219 "parser.rl"
 	{
         char *np = JSON_parse_string(json, p, pe, result);
         if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
     }
 	goto st21;
 tr2:
-#line 215 "parser.rl"
+#line 224 "parser.rl"
 	{
         char *np;
-        if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) {
+        if(pe > p + 9 - json->quirks_mode && !strncmp(MinusInfinity, p, 9)) {
             if (json->allow_nan) {
                 *result = CMinusInfinity;
                 {p = (( p + 10))-1;}
     }
 	goto st21;
 tr5:
-#line 233 "parser.rl"
-	{ 
+#line 242 "parser.rl"
+	{
         char *np;
         json->current_nesting++;
         np = JSON_parse_array(json, p, pe, result);
     }
 	goto st21;
 tr9:
-#line 241 "parser.rl"
-	{ 
+#line 250 "parser.rl"
+	{
         char *np;
         json->current_nesting++;
         np =  JSON_parse_object(json, p, pe, result);
     }
 	goto st21;
 tr16:
-#line 203 "parser.rl"
+#line 212 "parser.rl"
 	{
         if (json->allow_nan) {
             *result = CInfinity;
     }
 	goto st21;
 tr18:
-#line 196 "parser.rl"
+#line 205 "parser.rl"
 	{
         if (json->allow_nan) {
             *result = CNaN;
     }
 	goto st21;
 tr22:
-#line 190 "parser.rl"
+#line 199 "parser.rl"
 	{
         *result = Qfalse;
     }
 	goto st21;
 tr25:
-#line 187 "parser.rl"
+#line 196 "parser.rl"
 	{
         *result = Qnil;
     }
 	goto st21;
 tr28:
-#line 193 "parser.rl"
+#line 202 "parser.rl"
 	{
         *result = Qtrue;
     }
 	if ( ++p == pe )
 		goto _test_eof21;
 case 21:
-#line 249 "parser.rl"
+#line 258 "parser.rl"
 	{ p--; {p++; cs = 21; goto _out;} }
-#line 600 "parser.c"
+#line 608 "parser.c"
 	goto st0;
 st2:
 	if ( ++p == pe )
 		goto tr28;
 	goto st0;
 	}
-	_test_eof21: cs = 21; 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_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_eof: {}
 	_out: {}
 	}
 
-#line 270 "parser.rl"
+#line 279 "parser.rl"
 
     if (cs >= JSON_value_first_final) {
         return p;
 }
 
 
-#line 771 "parser.c"
+#line 779 "parser.c"
 static const int JSON_integer_start = 1;
-static const int JSON_integer_first_final = 5;
+static const int JSON_integer_first_final = 3;
 static const int JSON_integer_error = 0;
 
 static const int JSON_integer_en_main = 1;
 
 
-#line 286 "parser.rl"
+#line 295 "parser.rl"
 
 
 static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
 {
     int cs = EVIL;
 
-    
-#line 787 "parser.c"
+
+#line 795 "parser.c"
 	{
 	cs = JSON_integer_start;
 	}
 
-#line 293 "parser.rl"
+#line 302 "parser.rl"
     json->memo = p;
-    
-#line 795 "parser.c"
+
+#line 803 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
 		case 48: goto st3;
 	}
 	if ( 49 <= (*p) && (*p) <= 57 )
-		goto st4;
+		goto st5;
 	goto st0;
 st0:
 cs = 0;
 	if ( (*p) == 48 )
 		goto st3;</