[DRE-commits] [ruby-json] 01/05: New upstream version 2.1.0

Cédric Boutillier boutil at moszumanska.debian.org
Thu Jul 6 22:58:58 UTC 2017


This is an automated email from the git hooks/post-receive script.

boutil pushed a commit to branch master
in repository ruby-json.

commit 59f18f5f752185ac8ec3ce1e6320d38ff307b9d7
Author: Cédric Boutillier <boutil at debian.org>
Date:   Fri Jul 7 00:23:15 2017 +0200

    New upstream version 2.1.0
---
 .gitignore                                         |   1 +
 .travis.yml                                        |  13 +-
 CHANGES => CHANGES.md                              | 273 ++++---
 COPYING                                            |  58 --
 COPYING-json-jruby                                 |  57 --
 GPL                                                | 340 --------
 Gemfile                                            |  16 +-
 README-json-jruby.markdown => README-json-jruby.md |   0
 README.rdoc => README.md                           | 260 +++---
 Rakefile                                           |  68 +-
 TODO                                               |   1 -
 VERSION                                            |   2 +-
 ext/json/ext/fbuffer/fbuffer.h                     |   3 -
 ext/json/ext/generator/generator.c                 | 107 ++-
 ext/json/ext/generator/generator.h                 |   9 +-
 ext/json/ext/parser/extconf.rb                     |   3 +
 ext/json/ext/parser/parser.c                       | 813 ++++++++-----------
 ext/json/ext/parser/parser.h                       |   9 +-
 ext/json/ext/parser/parser.rl                      | 292 +++----
 ext/json/extconf.rb                                |   1 -
 java/src/json/ext/ByteListTranscoder.java          |   3 +-
 java/src/json/ext/Generator.java                   |  23 +-
 java/src/json/ext/GeneratorMethods.java            |   3 +-
 java/src/json/ext/GeneratorService.java            |   3 +-
 java/src/json/ext/GeneratorState.java              |  59 +-
 java/src/json/ext/OptionsReader.java               |   5 +-
 java/src/json/ext/Parser.java                      | 553 ++++---------
 java/src/json/ext/Parser.rl                        | 172 ++--
 java/src/json/ext/ParserService.java               |   3 +-
 java/src/json/ext/RuntimeInfo.java                 |   7 +-
 java/src/json/ext/StringDecoder.java               |   3 +-
 java/src/json/ext/StringEncoder.java               |   5 +
 java/src/json/ext/Utils.java                       |   3 +-
 json-java.gemspec                                  |  15 +
 json.gemspec                                       | Bin 5008 -> 5474 bytes
 json_pure.gemspec                                  |  50 +-
 lib/json.rb                                        |   1 +
 lib/json/add/bigdecimal.rb                         |   1 +
 lib/json/add/complex.rb                            |   3 +-
 lib/json/add/core.rb                               |   1 +
 lib/json/add/date.rb                               |   2 +-
 lib/json/add/date_time.rb                          |   2 +-
 lib/json/add/exception.rb                          |   2 +-
 lib/json/add/ostruct.rb                            |   4 +-
 lib/json/add/range.rb                              |   2 +-
 lib/json/add/rational.rb                           |   1 +
 lib/json/add/regexp.rb                             |   2 +-
 lib/json/add/struct.rb                             |   2 +-
 lib/json/add/symbol.rb                             |   2 +-
 lib/json/add/time.rb                               |   2 +-
 lib/json/common.rb                                 |  76 +-
 lib/json/ext.rb                                    |   6 -
 lib/json/generic_object.rb                         |   9 +-
 lib/json/pure.rb                                   |  10 +-
 lib/json/pure/generator.rb                         | 186 ++---
 lib/json/pure/parser.rb                            | 114 +--
 lib/json/version.rb                                |   3 +-
 metadata.yml                                       | 203 -----
 references/rfc7159.txt                             | 899 +++++++++++++++++++++
 tests/fixtures/fail1.json                          |   1 -
 tests/fixtures/obsolete_fail1.json                 |   1 +
 ...test_json_addition.rb => json_addition_test.rb} |  47 +-
 tests/json_common_interface_test.rb                | 126 +++
 tests/json_encoding_test.rb                        | 107 +++
 tests/json_ext_parser_test.rb                      |  15 +
 ...test_json_fixtures.rb => json_fixtures_test.rb} |  13 +-
 ...est_json_generate.rb => json_generator_test.rb} | 118 ++-
 ...neric_object.rb => json_generic_object_test.rb} |  23 +-
 tests/json_parser_test.rb                          | 471 +++++++++++
 tests/json_string_matching_test.rb                 |  38 +
 tests/{setup_variant.rb => test_helper.rb}         |  10 +
 tests/test_json.rb                                 | 553 -------------
 tests/test_json_encoding.rb                        |  65 --
 tests/test_json_string_matching.rb                 |  39 -
 tests/test_json_unicode.rb                         |  72 --
 tools/diff.sh                                      |  18 +
 tools/fuzz.rb                                      |  10 +-
 77 files changed, 3102 insertions(+), 3391 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5ef62af..69b608d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ Gemfile.lock
 .DS_Store
 */**/Makefile
 */**/*.o
+.byebug_history
diff --git a/.travis.yml b/.travis.yml
index 22cb59f..e2eeda5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,23 +4,16 @@ language: ruby
 
 # Specify which ruby versions you wish to run your tests on, each version will be used
 rvm:
-  - 1.8.7
-  - 1.9.2
   - 1.9.3
   - 2.0.0
   - 2.1
-  - 2.2.1
-  - 2.2.2
   - 2.2
-  - ree
-  - rbx-2
-  - jruby-18mode
-  - jruby-19mode
+  - 2.3.3
+  - 2.4.1
+  - jruby
   - ruby-head
 matrix:
   allow_failures:
-    - rvm: rbx-2
     - rvm: ruby-head
 script: "bundle exec rake"
-
 sudo: false
diff --git a/CHANGES b/CHANGES.md
similarity index 70%
rename from CHANGES
rename to CHANGES.md
index 8ffecf7..d96e9ae 100644
--- a/CHANGES
+++ b/CHANGES.md
@@ -1,94 +1,153 @@
-2015-06-01 (1.8.3)
+# Changes
+
+## 2017-04-18 (2.1.0)
+ * Allow passing of `decimal_class` option to specify a class as which to parse
+   JSON float numbers.
+## 2017-03-23 (2.0.4)
+ * Raise exception for incomplete unicode surrogates/character escape
+   sequences. This problem was reported by Daniel Gollahon (dgollahon).
+ * Fix arbitrary heap exposure problem. This problem was reported by Ahmad
+   Sherif (ahmadsherif).
+
+## 2017-01-12 (2.0.3)
+ * Set `required_ruby_version` to 1.9
+ * Some small fixes
+
+## 2016-07-26 (2.0.2)
+  * Specify `required_ruby_version` for json\_pure.
+  * Fix issue #295 failure when parsing frozen strings.
+
+## 2016-07-01 (2.0.1)
+  * Fix problem when requiring json\_pure and Parser constant was defined top
+    level.
+  * Add `RB_GC_GUARD` to avoid possible GC problem via Pete Johns.
+  * Store `current_nesting` on stack by Aaron Patterson.
+
+## 2015-09-11 (2.0.0)
+  * Now complies to newest JSON RFC 7159.
+  * Implements compatibiliy to ruby 2.4 integer unification.
+  * Drops support for old rubies whose life has ended, that is rubies < 2.0.
+    Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
+  * There were still some mentions of dual GPL licensing in the source, but JSON
+    has just the Ruby license that itself includes an explicit dual-licensing
+    clause that allows covered software to be distributed under the terms of
+    the Simplified BSD License instead for all ruby versions >= 1.9.3. This is
+    however a GPL compatible license according to the Free Software Foundation.
+    I changed these mentions to be consistent with the Ruby license setting in
+    the gemspec files which were already correct now.
+
+## 2015-06-01 (1.8.3)
   * Fix potential memory leak, thx to nobu.
-2015-01-08 (1.8.2)
+
+## 2015-01-08 (1.8.2)
   * Some performance improvements by Vipul A M <vipulnsward at gmail.com>.
   * Fix by Jason R. Clark <jclark at newrelic.com> to avoid mutation of
-    JSON.dump_default_options.
+    `JSON.dump_default_options`.
   * More tests by Michael Mac-Vicar <mmacvicar at gmail.com> and fixing
-    space_before accessor in generator.
-  * Performance on Jruby improvemed by Ben Browning <bbrownin at redhat.com>.
+    `space_before` accessor in generator.
+  * Performance on Jruby improved by Ben Browning <bbrownin at redhat.com>.
   * Some fixes to be compatible with the new Ruby 2.2 by Zachary Scott <e at zzak.io>
     and SHIBATA Hiroshi <hsbt at ruby-lang.org>.
-2013-05-13 (1.8.1)
+
+## 2013-05-13 (1.8.1)
   * Remove Rubinius exception since transcoding should be working now.
-2013-05-13 (1.8.0)
+
+## 2013-05-13 (1.8.0)
   * Fix https://github.com/flori/json/issues/162 reported by Marc-Andre
     Lafortune <github_rocks at marc-andre.ca>. Thanks!
   * Applied patches by Yui NARUSE <naruse at airemix.jp> to suppress warning with
     -Wchar-subscripts and better validate UTF-8 strings.
   * Applied patch by ginriki at github to remove unnecessary if.
-  * Add load/dump interface to JSON::GenericObject to make
-        serialize :some_attribute, JSON::GenericObject
-    work in Rails active models for convenient SomeModel#some_attribute.foo.bar
+  * Add load/dump interface to `JSON::GenericObject` to make
+        serialize :some_attribute, `JSON::GenericObject`
+    work in Rails active models for convenient `SomeModel#some_attribute.foo.bar`
     access to serialised JSON data.
-2013-02-04 (1.7.7)
+
+## 2013-02-04 (1.7.7)
   * Security fix for JSON create_additions default value and
-    JSON::GenericObject. It should not be possible to create additions unless
+    `JSON::GenericObject`. It should not be possible to create additions unless
     explicitely requested by setting the create_additions argument to true or
-    using the JSON.load/dump interface. If JSON::GenericObject is supposed to
+    using the JSON.load/dump interface. If `JSON::GenericObject` is supposed to
     be automatically deserialised, this has to be explicitely enabled by
     setting
-      JSON::GenericObject.json_creatable = true
+        JSON::GenericObject.json_creatable = true
     as well.
   * Remove useless assert in fbuffer implementation.
   * Apply patch attached to https://github.com/flori/json/issues#issue/155
     provided by John Shahid <jvshahid at gmail.com>, Thx!
   * Add license information to rubygems spec data, reported by Jordi Massaguer Pla <jmassaguerpla at suse.de>.
   * Improve documentation, thx to Zachary Scott <zachary at zacharyscott.net>.
-2012-11-29 (1.7.6)
-  * Add GeneratorState#merge alias for JRuby, fix state accessor methods. Thx to
+
+## 2012-11-29 (1.7.6)
+  * Add `GeneratorState#merge` alias for JRuby, fix state accessor methods. Thx to
    jvshahid at github.
   * Increase hash likeness of state objects.
-2012-08-17 (1.7.5)
+
+## 2012-08-17 (1.7.5)
   * Fix compilation of extension on older rubies.
-2012-07-26 (1.7.4)
+
+## 2012-07-26 (1.7.4)
   * Fix compilation problem on AIX, see https://github.com/flori/json/issues/142
-2012-05-12 (1.7.3)
+
+## 2012-05-12 (1.7.3)
   * Work around Rubinius encoding issues using iconv for conversion instead.
-2012-05-11 (1.7.2)
+
+## 2012-05-11 (1.7.2)
   * Fix some encoding issues, that cause problems for  the pure and the
     extension variant in jruby 1.9 mode.
-2012-04-28 (1.7.1)
+
+## 2012-04-28 (1.7.1)
   * Some small fixes for building
-2012-04-28 (1.7.0)
-  * Add JSON::GenericObject for method access to objects transmitted via JSON.
-2012-04-27 (1.6.7)
+
+## 2012-04-28 (1.7.0)
+  * Add `JSON::GenericObject` for method access to objects transmitted via JSON.
+
+## 2012-04-27 (1.6.7)
   * Fix possible crash when trying to parse nil value.
-2012-02-11 (1.6.6)
+
+## 2012-02-11 (1.6.6)
   * Propagate src encoding to values made from it (fixes 1.9 mode converting
     everything to ascii-8bit; harmless for 1.8 mode too) (Thomas E. Enebo
     <tom.enebo at gmail.com>), should fix
     https://github.com/flori/json/issues#issue/119.
   * Fix https://github.com/flori/json/issues#issue/124 Thx to Jason Hutchens.
   * Fix https://github.com/flori/json/issues#issue/117
-2012-01-15 (1.6.5)
+
+## 2012-01-15 (1.6.5)
   * Vit Ondruch <v.ondruch at tiscali.cz> reported a bug that shows up when using
     optimisation under GCC 4.7. Thx to him, Bohuslav Kabrda
     <bkabrda at redhat.com> and Yui NARUSE <naruse at airemix.jp> for debugging and
     developing a patch fix.
-2011-12-24 (1.6.4)
+
+## 2011-12-24 (1.6.4)
   * Patches that improve speed on JRuby contributed by Charles Oliver Nutter
     <headius at headius.com>.
-  * Support object_class/array_class with duck typed hash/array.
-2011-12-01 (1.6.3)
-  * Let JSON.load('') return nil as well to make mysql text columns (default to
-    '') work better for serialization.
-2011-11-21 (1.6.2)
+  * Support `object_class`/`array_class` with duck typed hash/array.
+
+## 2011-12-01 (1.6.3)
+  * Let `JSON.load('')` return nil as well to make mysql text columns (default to
+    `''`) work better for serialization.
+
+## 2011-11-21 (1.6.2)
   * Add support for OpenStruct and BigDecimal.
-  * Fix bug when parsing nil in quirks_mode.
+  * Fix bug when parsing nil in `quirks_mode`.
   * Make JSON.dump and JSON.load methods better cooperate with Rails' serialize
-    method. Just use: serialize :value, JSON
+    method. Just use:
+        serialize :value, JSON
   * Fix bug with time serialization concerning nanoseconds. Thanks for the
     patch go to Josh Partlow (jpartlow at github).
   * Improve parsing speed for JSON numbers (integers and floats) in a similar way to
     what Evan Phoenix <evan at phx.io> suggested in:
     https://github.com/flori/json/pull/103
-2011-09-18 (1.6.1)
+
+## 2011-09-18 (1.6.1)
   * Using -target 1.5 to force Java bits to compile with 1.5.
-2011-09-12 (1.6.0)
+
+## 2011-09-12 (1.6.0)
   * Extract utilities (prettifier and GUI-editor) in its own gem json-utils.
   * Split json/add/core into different files for classes to be serialised.
-2011-08-31 (1.5.4)
+
+## 2011-08-31 (1.5.4)
   * Fix memory leak when used from multiple JRuby. (Patch by
     jfirebaugh at github).
   * Apply patch by Eric Wong <nocode at yhbt.net> that fixes garbage collection problem
@@ -96,42 +155,50 @@
   * Add :quirks_mode option to parser and generator.
   * Add support for Rational and Complex number additions via json/add/complex
     and json/add/rational requires.
-2011-06-20 (1.5.3)
+
+## 2011-06-20 (1.5.3)
   * Alias State#configure method as State#merge to increase duck type synonymy with Hash.
-	* Add as_json methods in json/add/core, so rails can create its json objects
+	* Add `as_json` methods in json/add/core, so rails can create its json objects
 	  the new way.
-2011-05-11 (1.5.2)
+
+## 2011-05-11 (1.5.2)
   * Apply documentation patch by Cory Monty <cory.monty at gmail.com>.
-  * Add gemspecs for json and json_pure.
+  * Add gemspecs for json and json\_pure.
   * Fix bug in jruby pretty printing.
-  * Fix bug in object_class and array_class when inheriting from Hash or Array.
-2011-01-24 (1.5.1)
+  * Fix bug in `object_class` and `array_class` when inheriting from Hash or
+    Array.
+
+## 2011-01-24 (1.5.1)
   * Made rake-compiler build a fat binary gem. This should fix issue
     https://github.com/flori/json/issues#issue/54.
-2011-01-22 (1.5.0)
+
+## 2011-01-22 (1.5.0)
   * Included Java source codes for the Jruby extension made by Daniel Luz
     <dev at mernen.com>.
-  * Output full exception message of deep_const_get to aid debugging.
-  * Fixed an issue with ruby 1.9 Module#const_defined? method, that was
+  * Output full exception message of `deep_const_get` to aid debugging.
+  * Fixed an issue with ruby 1.9 `Module#const_defined?` method, that was
     reported by Riley Goodside.
-2010-08-09 (1.4.6)
+
+## 2010-08-09 (1.4.6)
   * Fixed oversight reported in http://github.com/flori/json/issues/closed#issue/23,
     always create a new object from the state prototype.
   * Made pure and ext api more similar again.
-2010-08-07 (1.4.5)
+
+## 2010-08-07 (1.4.5)
   * Manage data structure nesting depth in state object during generation. This
-    should reduce problems with to_json method definіtions that only have one
+    should reduce problems with `to_json` method definіtions that only have one
     argument.
   * Some fixes in the state objects and additional tests.
-2010-08-06 (1.4.4)
+## 2010-08-06 (1.4.4)
   * Fixes build problem for rubinius under OS X, http://github.com/flori/json/issues/closed#issue/25
   * Fixes crashes described in http://github.com/flori/json/issues/closed#issue/21 and
     http://github.com/flori/json/issues/closed#issue/23
-2010-05-05 (1.4.3)
+## 2010-05-05 (1.4.3)
   * Fixed some test assertions, from Ruby r27587 and r27590, patch by nobu.
   * Fixed issue http://github.com/flori/json/issues/#issue/20 reported by
     electronicwhisper at github. Thx!
-2010-04-26 (1.4.2)
+
+## 2010-04-26 (1.4.2)
   * Applied patch from naruse Yui NARUSE <naruse at airemix.com> to make building with
     Microsoft Visual C possible again.
   * Applied patch from devrandom <c1.github at niftybox.net> in order to allow building of
@@ -139,34 +206,43 @@
   * Thanks to Dustin Schneider <dustin at stocktwits.com>, who reported a memory
     leak, which is fixed in this release.
   * Applied 993f261ccb8f911d2ae57e9db48ec7acd0187283 patch from josh at github.
-2010-04-25 (1.4.1)
+
+## 2010-04-25 (1.4.1)
   * Fix for a bug reported by Dan DeLeo <dan at kallistec.com>, caused by T_FIXNUM
     being different on 32bit/64bit architectures.
-2010-04-23 (1.4.0)
+
+## 2010-04-23 (1.4.0)
   * Major speed improvements and building with simplified
     directory/file-structure.
   * Extension should at least be comapatible with MRI, YARV and Rubinius.
-2010-04-07 (1.2.4)
+
+## 2010-04-07 (1.2.4)
   * Triger const_missing callback to make Rails' dynamic class loading work.
-2010-03-11 (1.2.3)
-  * Added a State#[] method which returns an attribute's value in order to
+
+## 2010-03-11 (1.2.3)
+  * Added a `State#[]` method which returns an attribute's value in order to
     increase duck type compatibility to Hash.
-2010-02-27 (1.2.2)
+
+## 2010-02-27 (1.2.2)
   * Made some changes to make the building of the parser/generator compatible
     to Rubinius.
-2009-11-25 (1.2.1)
-  * Added :symbolize_names option to Parser, which returns symbols instead of
+
+## 2009-11-25 (1.2.1)
+  * Added `:symbolize_names` option to Parser, which returns symbols instead of
     strings in object names/keys.
-2009-10-01 (1.2.0)
-  * fast_generate now raises an exeception for nan and infinite floats.
+
+## 2009-10-01 (1.2.0)
+  * `fast_generate` now raises an exeception for nan and infinite floats.
   * On Ruby 1.8 json supports parsing of UTF-8, UTF-16BE, UTF-16LE, UTF-32BE,
     and UTF-32LE JSON documents now. Under Ruby 1.9 the M17n conversion
     functions are used to convert from all supported encodings. ASCII-8BIT
     encoded strings are handled like all strings under Ruby 1.8 were.
   * Better documentation
-2009-08-23 (1.1.9)
-  * Added forgotten main doc file extra_rdoc_files.
-2009-08-23 (1.1.8)
+
+## 2009-08-23 (1.1.9)
+  * Added forgotten main doc file `extra_rdoc_files`.
+
+## 2009-08-23 (1.1.8)
   * Applied a patch by OZAWA Sakuro <sakuro at 2238club.org> to make json/pure
     work in environments that don't provide iconv.
   * Applied patch by okkez_ in order to fix Ruby Bug #1768:
@@ -174,7 +250,8 @@
   * Finally got around to avoid the rather paranoid escaping of ?/ characters
     in the generator's output. The parsers aren't affected by this change.
     Thanks to Rich Apodaca <rapodaca at metamolecular.com> for the suggestion.
-2009-06-29 (1.1.7)
+
+## 2009-06-29 (1.1.7)
   * Security Fix for JSON::Pure::Parser. A specially designed string could
     cause catastrophic backtracking in one of the parser's regular expressions
     in earlier 1.1.x versions. JSON::Ext::Parser isn't affected by this issue.
@@ -182,16 +259,19 @@
     problem.
   * This release also uses a less strict ruby version requirement for the
     creation of the mswin32 native gem.
-2009-05-10 (1.1.6)
+
+## 2009-05-10 (1.1.6)
   * No changes. І tested native linux gems in the last release and they don't
     play well with different ruby versions other than the one the gem was built
     with. This release is just to bump the version number in order to skip the
     native gem on rubyforge.
-2009-05-10 (1.1.5)
+
+## 2009-05-10 (1.1.5)
   * Started to build gems with rake-compiler gem.
   * Applied patch object/array class patch from Brian Candler
     <B.Candler at pobox.com> and fixes.
-2009-04-01 (1.1.4)
+
+## 2009-04-01 (1.1.4)
   * Fixed a bug in the creation of serialized generic rails objects reported by
     Friedrich Graeter <graeter at hydrixos.org>.
   * Deleted tests/runner.rb, we're using testrb instead.
@@ -200,7 +280,8 @@
     1.9.
   * Improved speed of the code path for the fast_generate method in the pure
     variant.
-2008-07-10 (1.1.3)
+
+## 2008-07-10 (1.1.3)
   * Wesley Beary <monki at geemus.com> reported a bug in json/add/core's DateTime
     handling: If the nominator and denominator of the offset were divisible by
     each other Ruby's Rational#to_s returns them as an integer not a fraction
@@ -210,7 +291,8 @@
   * Supports ragel >= 6.0 now.
   * Corrected some tests.
   * Some minor changes.
-2007-11-27 (1.1.2)
+
+## 2007-11-27 (1.1.2)
   * Remember default dir (last used directory) in editor.
   * JSON::Editor.edit method added, the editor can now receive json texts from
     the clipboard via C-v.
@@ -226,13 +308,14 @@
     generator by returning something other than a String instance from a
     to_json method. I now guard against this by doing a rather crude type
     check, which raises an exception instead of crashing.
-2007-07-06 (1.1.1)
+
+## 2007-07-06 (1.1.1)
   * Yui NARUSE <naruse at airemix.com> sent some patches to fix tests for Ruby
     1.9. I applied them and adapted some of them a bit to run both on 1.8 and
     1.9.
-  * Introduced a JSON.parse! method without depth checking for people who like
-    danger.
-  * Made generate and pretty_generate methods configurable by an options hash.
+  * Introduced a `JSON.parse!` method without depth checking for people who
+    like danger.
+  * Made generate and `pretty_generate` methods configurable by an options hash.
   * Added :allow_nan option to parser and generator in order to handle NaN,
     Infinity, and -Infinity correctly - if requested. Floats, which aren't numbers,
     aren't valid JSON according to RFC4627, so by default an exception will be
@@ -241,55 +324,65 @@
   * Fixed some more tests for Ruby 1.9.
   * Implemented dump/load interface of Marshal as suggested in ruby-core:11405
     by murphy <murphy at rubychan.de>.
-  * Implemented the max_nesting feature for generate methods, too.
+  * Implemented the `max_nesting` feature for generate methods, too.
   * Added some implementations for ruby core's custom objects for
     serialisation/deserialisation purposes.
-2007-05-21 (1.1.0)
+
+## 2007-05-21 (1.1.0)
   * Implemented max_nesting feature for parser to avoid stack overflows for
     data from untrusted sources. If you trust the source, you can disable it
     with the option max_nesting => false.
   * Piers Cawley <pdcawley at bofh.org.uk> reported a bug, that not every
-    character can be escaped by ?\ as required by RFC4627. There's a
+    character can be escaped by `\` as required by RFC4627. There's a
     contradiction between David Crockford's JSON checker test vectors (in
     tests/fixtures) and RFC4627, though. I decided to stick to the RFC, because
     the JSON checker seems to be a bit older than the RFC.
   * Extended license to Ruby License, which includes the GPL.
   * Added keyboard shortcuts, and 'Open location' menu item to edit_json.rb.
-2007-05-09 (1.0.4)
+
+## 2007-05-09 (1.0.4)
   * Applied a patch from Yui NARUSE <naruse at airemix.com> to make JSON compile
     under Ruby 1.9. Thank you very much for mailing it to me!
   * Made binary variants of JSON fail early, instead of falling back to the
     pure version. This should avoid overshadowing of eventual problems while
     loading of the binary.
-2007-03-24 (1.0.3)
+
+## 2007-03-24 (1.0.3)
   * Improved performance of pure variant a bit.
   * The ext variant of this release supports the mswin32 platform. Ugh!
-2007-03-24 (1.0.2)
+
+## 2007-03-24 (1.0.2)
   * Ext Parser didn't parse 0e0 correctly into 0.0: Fixed!
-2007-03-24 (1.0.1)
+
+## 2007-03-24 (1.0.1)
   * Forgot some object files in the build dir. I really like that - not!
-2007-03-24 (1.0.0)
+
+## 2007-03-24 (1.0.0)
   * Added C implementations for the JSON generator and a ragel based JSON
     parser in C.
   * Much more tests, especially fixtures from json.org.
   * Further improved conformance to RFC4627.
-2007-02-09 (0.4.3)
+
+## 2007-02-09 (0.4.3)
   * Conform more to RFC4627 for JSON: This means JSON strings
-    now always must contain exactly one object "{ ... }" or array "[ ... ]" in
+    now always must contain exactly one object `"{ ... }"` or array `"[ ... ]"` in
     order to be parsed without raising an exception. The definition of what
     constitutes a whitespace is narrower in JSON than in Ruby ([ \t\r\n]), and
     there are differences in floats and integers (no octals or hexadecimals) as
     well.
-  * Added aliases generate and pretty_generate of unparse and pretty_unparse.
+  * Added aliases generate and `pretty_generate` of unparse and `pretty_unparse`.
   * Fixed a test case.
-  * Catch an Iconv::InvalidEncoding exception, that seems to occur on some Sun
+  * Catch an `Iconv::InvalidEncoding` exception, that seems to occur on some Sun
     boxes with SunOS 5.8, if iconv doesn't support utf16 conversions. This was
     reported by Andrew R Jackson <andrewj at bcm.tmc.edu>, thanks a bunch!
-2006-08-25 (0.4.2)
+
+## 2006-08-25 (0.4.2)
   * Fixed a bug in handling solidi (/-characters), that was reported by
     Kevin Gilpin <kevin.gilpin at alum.mit.edu>.
-2006-02-06 (0.4.1)
+
+## 2006-02-06 (0.4.1)
   * Fixed a bug related to escaping with backslashes. Thanks for the report go
     to Florian Munz <surf at theflow.de>.
-2005-09-23 (0.4.0)
+
+## 2005-09-23 (0.4.0)
   * Initial Rubyforge Version
diff --git a/COPYING b/COPYING
deleted file mode 100644
index c3a2126..0000000
--- a/COPYING
+++ /dev/null
@@ -1,58 +0,0 @@
-Ruby is copyrighted free software by Yukihiro Matsumoto <matz at netlab.co.jp>.
-You can redistribute it and/or modify it under either the terms of the GPL
-(see GPL file), or the conditions below:
-
-  1. You may make and give away verbatim copies of the source form of the
-     software without restriction, provided that you duplicate all of the
-     original copyright notices and associated disclaimers.
-
-  2. You may modify your copy of the software in any way, provided that
-     you do at least ONE of the following:
-
-       a) place your modifications in the Public Domain or otherwise
-          make them Freely Available, such as by posting said
-	  modifications to Usenet or an equivalent medium, or by allowing
-	  the author to include your modifications in the software.
-
-       b) use the modified software only within your corporation or
-          organization.
-
-       c) rename any non-standard executables so the names do not conflict
-	  with standard executables, which must also be provided.
-
-       d) make other distribution arrangements with the author.
-
-  3. You may distribute the software in object code or executable
-     form, provided that you do at least ONE of the following:
-
-       a) distribute the executables and library files of the software,
-	  together with instructions (in the manual page or equivalent)
-	  on where to get the original distribution.
-
-       b) accompany the distribution with the machine-readable source of
-	  the software.
-
-       c) give non-standard executables non-standard names, with
-          instructions on where to get the original software distribution.
-
-       d) make other distribution arrangements with the author.
-
-  4. You may modify and include the part of the software into any other
-     software (possibly commercial).  But some files in the distribution
-     are not written by the author, so that they are not under this terms.
-
-     They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
-     files under the ./missing directory.  See each file for the copying
-     condition.
-
-  5. The scripts and library files supplied as input to or produced as 
-     output from the software do not automatically fall under the
-     copyright of the software, but belong to whomever generated them, 
-     and may be sold commercially, and may be aggregated with this
-     software.
-
-  6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-     IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-     PURPOSE.
-
diff --git a/COPYING-json-jruby b/COPYING-json-jruby
deleted file mode 100644
index 137a3da..0000000
--- a/COPYING-json-jruby
+++ /dev/null
@@ -1,57 +0,0 @@
-JSON-JRuby is copyrighted free software by Daniel Luz <mernen at gmail dot com>,
-and is a derivative work of Florian Frank's json library <flori at ping dot de>.
-You can redistribute it and/or modify it under either the terms of the GPL
-version 2 (see the file GPL), or the conditions below:
-
-  1. You may make and give away verbatim copies of the source form of the
-     software without restriction, provided that you duplicate all of the
-     original copyright notices and associated disclaimers.
-
-  2. You may modify your copy of the software in any way, provided that
-     you do at least ONE of the following:
-
-       a) place your modifications in the Public Domain or otherwise
-          make them Freely Available, such as by posting said
-	  modifications to Usenet or an equivalent medium, or by allowing
-	  the author to include your modifications in the software.
-
-       b) use the modified software only within your corporation or
-          organization.
-
-       c) give non-standard binaries non-standard names, with
-          instructions on where to get the original software distribution.
-
-       d) make other distribution arrangements with the author.
-
-  3. You may distribute the software in object code or binary form,
-     provided that you do at least ONE of the following:
-
-       a) distribute the binaries and library files of the software,
-	  together with instructions (in the manual page or equivalent)
-	  on where to get the original distribution.
-
-       b) accompany the distribution with the machine-readable source of
-	  the software.
-
-       c) give non-standard binaries non-standard names, with
-          instructions on where to get the original software distribution.
-
-       d) make other distribution arrangements with the author.
-
-  4. You may modify and include the part of the software into any other
-     software (possibly commercial).  But some files in the distribution
-     are not written by the author, so that they are not under these terms.
-
-     For the list of those files and their copying conditions, see the
-     file LEGAL.
-
-  5. The scripts and library files supplied as input to or produced as 
-     output from the software do not automatically fall under the
-     copyright of the software, but belong to whomever generated them, 
-     and may be sold commercially, and may be aggregated with this
-     software.
-
-  6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-     IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-     PURPOSE.
diff --git a/GPL b/GPL
deleted file mode 100644
index db2fc45..0000000
--- a/GPL
+++ /dev/null
@@ -1,340 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-

-                    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-

-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-

-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-

-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                            NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-

-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/Gemfile b/Gemfile
index ef9ce01..64f98d6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,9 +2,15 @@
 
 source 'https://rubygems.org'
 
-gemspec :name => 'json'
-gemspec :name => 'json_pure'
-gemspec :name => 'json-java'
+case ENV['JSON']
+when 'ext', nil
+  if ENV['RUBY_ENGINE'] == 'jruby'
+    gemspec :name => 'json-java'
+  else
+    gemspec :name => 'json'
+  end
+when 'pure'
+  gemspec :name => 'json_pure'
+end
 
-gem 'utils'
-gem 'test-unit'
+gem 'simplecov'
diff --git a/README-json-jruby.markdown b/README-json-jruby.md
similarity index 100%
rename from README-json-jruby.markdown
rename to README-json-jruby.md
diff --git a/README.rdoc b/README.md
similarity index 56%
rename from README.rdoc
rename to README.md
index ebb8b82..f9cd95d 100644
--- a/README.rdoc
+++ b/README.md
@@ -1,9 +1,10 @@
-= JSON implementation for Ruby {<img src="https://secure.travis-ci.org/flori/json.png" />}[http://travis-ci.org/flori/json]
+# JSON implementation for Ruby ![Travis Widget]
+[Travis Widget]: http://travis-ci.org/flori/json.svg?branch=master
 
-== Description
+## Description
 
-This is a implementation of the JSON specification according to RFC 4627
-http://www.ietf.org/rfc/rfc4627.txt . Starting from version 1.0.0 on there
+This is a implementation of the JSON specification according to RFC 7159
+http://www.ietf.org/rfc/rfc7159.txt . Starting from version 1.0.0 on there
 will be two variants available:
 
 * A pure ruby variant, that relies on the iconv and the stringscan
@@ -11,28 +12,21 @@ will be two variants available:
 * The quite a bit faster native extension variant, which is in parts
   implemented in C or Java and comes with its own unicode conversion
   functions and a parser generated by the ragel state machine compiler
-  http://www.cs.queensu.ca/~thurston/ragel .
+  http://www.complang.org/ragel/ .
 
 Both variants of the JSON generator generate UTF-8 character sequences by
-default. If an :ascii_only option with a true value is given, they escape all
+default. If an :ascii\_only option with a true value is given, they escape all
 non-ASCII and control characters with \uXXXX escape sequences, and support
 UTF-16 surrogate pairs in order to be able to generate the whole range of
 unicode code points.
 
 All strings, that are to be encoded as JSON strings, should be UTF-8 byte
 sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8
-encoded, please use the to_json_raw_object method of String (which produces
+encoded, please use the to\_json\_raw\_object method of String (which produces
 an object, that contains a byte array) and decode the result on the receiving
 endpoint.
 
-The JSON parsers can parse UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, and UTF-32LE
-JSON documents under Ruby 1.8. Under Ruby 1.9 they take advantage of Ruby's
-M17n features and can parse all documents which have the correct
-String#encoding set. If a document string has ASCII-8BIT as an encoding the
-parser attempts to figure out which of the UTF encodings from above it is and
-trys to parse it.
-
-== Installation
+## Installation
 
 It's recommended to use the extension variant of JSON, because it's faster than
 the pure ruby variant. If you cannot build it on your system, you can settle
@@ -40,124 +34,151 @@ for the latter.
 
 Just type into the command line as root:
 
-  # rake install
+```
+# rake install
+```
 
 The above command will build the extensions and install them on your system.
 
-  # rake install_pure
+```
+# rake install_pure
+```
 
 or
 
-  # ruby install.rb
+```
+# ruby install.rb
+```
 
 will just install the pure ruby implementation of JSON.
 
 If you use Rubygems you can type
 
-  # gem install json
+```
+# gem install json
+```
 
 instead, to install the newest JSON version.
 
 There is also a pure ruby json only variant of the gem, that can be installed
 with:
 
-  # gem install json_pure
-
-== Compiling the extensions yourself
+```
+# gem install json_pure
+```
 
-If you want to build the extensions yourself you need rake:
+## Compiling the extensions yourself
 
-  You can get it from rubyforge:
-    http://rubyforge.org/projects/rake
+If you want to create the `parser.c` file from its `parser.rl` file or draw nice
+graphviz images of the state machines, you need ragel from:
+http://www.complang.org/ragel/
 
-  or just type
+## Usage
 
-  # gem install rake
-
-  for the installation via rubygems.
+To use JSON you can
 
-If you want to create the parser.c file from its parser.rl file or draw nice
-graphviz images of the state machines, you need ragel from: http://www.cs.queensu.ca/~thurston/ragel
+```ruby
+require 'json'
+```
 
+to load the installed variant (either the extension `'json'` or the pure
+variant `'json_pure'`). If you have installed the extension variant, you can
+pick either the extension variant or the pure variant by typing
 
-== Usage
+```ruby
+require 'json/ext'
+```
 
-To use JSON you can
-  require 'json'
-to load the installed variant (either the extension 'json' or the pure
-variant 'json_pure'). If you have installed the extension variant, you can
-pick either the extension variant or the pure variant by typing
-  require 'json/ext'
 or
-  require 'json/pure'
+
+```ruby
+require 'json/pure'
+```
 
 Now you can parse a JSON document into a ruby data structure by calling
 
-  JSON.parse(document)
+```ruby
+JSON.parse(document)
+```
 
 If you want to generate a JSON document from a ruby data structure call
-  JSON.generate(data)
+```ruby
+JSON.generate(data)
+```
 
-You can also use the pretty_generate method (which formats the output more
-verbosely and nicely) or fast_generate (which doesn't do any of the security
+You can also use the `pretty_generate` method (which formats the output more
+verbosely and nicely) or `fast_generate` (which doesn't do any of the security
 checks generate performs, e. g. nesting deepness checks).
 
-To create a valid JSON document you have to make sure, that the output is
-embedded in either a JSON array [] or a JSON object {}. The easiest way to do
-this, is by putting your values in a Ruby Array or Hash instance.
-
 There are also the JSON and JSON[] methods which use parse on a String or
 generate a JSON document from an array or hash:
 
-  document = JSON 'test'  => 23 # => "{\"test\":23}"
-  document = JSON['test'] => 23 # => "{\"test\":23}"
+```ruby
+document = JSON 'test'  => 23 # => "{\"test\":23}"
+document = JSON['test' => 23] # => "{\"test\":23}"
+```
 
 and
 
-  data = JSON '{"test":23}'  # => {"test"=>23}
-  data = JSON['{"test":23}'] # => {"test"=>23}
+```ruby
+data = JSON '{"test":23}'  # => {"test"=>23}
+data = JSON['{"test":23}'] # => {"test"=>23}
+```
 
 You can choose to load a set of common additions to ruby core's objects if
 you
-  require 'json/add/core'
+
+```ruby
+require 'json/add/core'
+```
 
 After requiring this you can, e. g., serialise/deserialise Ruby ranges:
 
-  JSON JSON(1..10) # => 1..10
+```ruby
+JSON JSON(1..10) # => 1..10
+```
 
 To find out how to add JSON support to other or your own classes, read the
 section "More Examples" below.
 
 To get the best compatibility to rails' JSON implementation, you can
-  require 'json/add/rails'
 
-Both of the additions attempt to require 'json' (like above) first, if it has
+```ruby
+require 'json/add/rails'
+```
+
+Both of the additions attempt to require `'json'` (like above) first, if it has
 not been required yet.
 
-== More Examples
+## More Examples
 
 To create a JSON document from a ruby data structure, you can call
-JSON.generate like that:
+`JSON.generate` like that:
 
- json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
- # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
+```ruby
+json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
+# => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
+```
 
 To get back a ruby data structure from a JSON document, you have to call
 JSON.parse on it:
 
- JSON.parse json
- # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
+```ruby
+JSON.parse json
+# => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
+```
 
 Note, that the range from the original data structure is a simple
 string now. The reason for this is, that JSON doesn't support ranges
 or arbitrary classes. In this case the json library falls back to call
-Object#to_json, which is the same as #to_s.to_json.
+`Object#to_json`, which is the same as `#to_s.to_json`.
 
 It's possible to add JSON support serialization to arbitrary classes by
-simply implementing a more specialized version of the #to_json method, that
-should return a JSON object (a hash converted to JSON with #to_json) like
-this (don't forget the *a for all the arguments):
+simply implementing a more specialized version of the `#to_json method`, that
+should return a JSON object (a hash converted to JSON with `#to_json`) like
+this (don't forget the `*a` for all the arguments):
 
+```ruby
  class Range
    def to_json(*a)
      {
@@ -166,36 +187,42 @@ this (don't forget the *a for all the arguments):
      }.to_json(*a)
    end
  end
+```
 
-The hash key 'json_class' is the class, that will be asked to deserialise the
-JSON representation later. In this case it's 'Range', but any namespace of
-the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be
+The hash key `json_class` is the class, that will be asked to deserialise the
+JSON representation later. In this case it's `Range`, but any namespace of
+the form `A::B` or `::A::B` will do. All other keys are arbitrary and can be
 used to store the necessary data to configure the object to be deserialised.
 
-If a the key 'json_class' is found in a JSON object, the JSON parser checks
-if the given class responds to the json_create class method. If so, it is
+If a the key `json_class` is found in a JSON object, the JSON parser checks
+if the given class responds to the `json_create` class method. If so, it is
 called with the JSON object converted to a Ruby hash. So a range can
-be deserialised by implementing Range.json_create like this:
+be deserialised by implementing `Range.json_create` like this:
 
+```ruby
  class Range
    def self.json_create(o)
      new(*o['data'])
    end
  end
+```
 
 Now it possible to serialise/deserialise ranges as well:
 
+```ruby
  json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
  # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
  JSON.parse json
  # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
+```
 
-JSON.generate always creates the shortest possible string representation of a
+`JSON.generate` always creates the shortest possible string representation of a
 ruby data structure in one line. This is good for data storage or network
 protocols, but not so good for humans to read. Fortunately there's also
-JSON.pretty_generate (or JSON.pretty_generate) that creates a more readable
+`JSON.pretty_generate` (or `JSON.pretty_generate`) that creates a more readable
 output:
 
+```ruby
  puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
  [
    1,
@@ -215,82 +242,88 @@ output:
      ]
    }
  ]
+```
 
-There are also the methods Kernel#j for generate, and Kernel#jj for
-pretty_generate output to the console, that work analogous to Core Ruby's p and
-the pp library's pp methods.
+There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
+`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
+the `pp` library's `pp` methods.
 
-The script tools/server.rb contains a small example if you want to test, how
+The script `tools/server.rb` contains a small example if you want to test, how
 receiving a JSON object from a webrick server in your browser with the
 javasript prototype library http://www.prototypejs.org works.
 
-== Speed Comparisons
+## Speed Comparisons
 
 I have created some benchmark results (see the benchmarks/data-p4-3Ghz
 subdir of the package) for the JSON-parser to estimate the speed up in the C
 extension:
 
+```
  Comparing times (call_time_mean):
   1 ParserBenchmarkExt#parser   900 repeats:
-        553.922304770 (  real) ->   21.500x 
+        553.922304770 (  real) ->   21.500x
           0.001805307
   2 ParserBenchmarkYAML#parser  1000 repeats:
-        224.513358139 (  real) ->    8.714x 
+        224.513358139 (  real) ->    8.714x
           0.004454078
   3 ParserBenchmarkPure#parser  1000 repeats:
-         26.755020642 (  real) ->    1.038x 
+         26.755020642 (  real) ->    1.038x
           0.037376163
   4 ParserBenchmarkRails#parser 1000 repeats:
-         25.763381731 (  real) ->    1.000x 
+         25.763381731 (  real) ->    1.000x
           0.038814780
             calls/sec (  time) ->    speed  covers
             secs/call
+```
 
-In the table above 1 is JSON::Ext::Parser, 2 is YAML.load with YAML
-compatbile JSON document, 3 is is JSON::Pure::Parser, and 4 is
-ActiveSupport::JSON.decode. The ActiveSupport JSON-decoder converts the
+In the table above 1 is `JSON::Ext::Parser`, 2 is `YAML.load` with YAML
+compatbile JSON document, 3 is is `JSON::Pure::Parser`, and 4 is
+`ActiveSupport::JSON.decode`. The ActiveSupport JSON-decoder converts the
 input first to YAML and then uses the YAML-parser, the conversion seems to
-slow it down so much that it is only as fast as the JSON::Pure::Parser!
+slow it down so much that it is only as fast as the `JSON::Pure::Parser`!
 
 If you look at the benchmark data you can see that this is mostly caused by
 the frequent high outliers - the median of the Rails-parser runs is still
-overall smaller than the median of the JSON::Pure::Parser runs:
+overall smaller than the median of the `JSON::Pure::Parser` runs:
 
+```
  Comparing times (call_time_median):
   1 ParserBenchmarkExt#parser   900 repeats:
-        800.592479481 (  real) ->   26.936x 
+        800.592479481 (  real) ->   26.936x
           0.001249075
   2 ParserBenchmarkYAML#parser  1000 repeats:
-        271.002390644 (  real) ->    9.118x 
+        271.002390644 (  real) ->    9.118x
           0.003690004
   3 ParserBenchmarkRails#parser 1000 repeats:
-         30.227910865 (  real) ->    1.017x 
+         30.227910865 (  real) ->    1.017x
           0.033082008
   4 ParserBenchmarkPure#parser  1000 repeats:
-         29.722384421 (  real) ->    1.000x 
+         29.722384421 (  real) ->    1.000x
           0.033644676
             calls/sec (  time) ->    speed  covers
             secs/call
+```
 
-I have benchmarked the JSON-Generator as well. This generated a few more
+I have benchmarked the `JSON-Generator` as well. This generated a few more
 values, because there are different modes that also influence the achieved
 speed:
 
+```
  Comparing times (call_time_mean):
   1 GeneratorBenchmarkExt#generator_fast    1000 repeats:
-        547.354332608 (  real) ->   15.090x 
+        547.354332608 (  real) ->   15.090x
           0.001826970
   2 GeneratorBenchmarkExt#generator_safe    1000 repeats:
-        443.968212317 (  real) ->   12.240x 
+        443.968212317 (  real) ->   12.240x
           0.002252414
   3 GeneratorBenchmarkExt#generator_pretty  900 repeats:
-        375.104545883 (  real) ->   10.341x 
+        375.104545883 (  real) ->   10.341x
           0.002665923
   4 GeneratorBenchmarkPure#generator_fast   1000 repeats:
-         49.978706968 (  real) ->    1.378x 
+         49.978706968 (  real) ->    1.378x
           0.020008521
   5 GeneratorBenchmarkRails#generator       1000 repeats:
-         38.531868759 (  real) ->    1.062x 
+         38.531868759 (  real) ->    1.062x
           0.025952543
   6 GeneratorBenchmarkPure#generator_safe   1000 repeats:
          36.927649925 (  real) ->    1.018x 7 (>=3859)
@@ -300,33 +333,35 @@ speed:
           0.027569373
             calls/sec (  time) ->    speed  covers
             secs/call
+```
 
-In the table above 1-3 are JSON::Ext::Generator methods. 4, 6, and 7 are
-JSON::Pure::Generator methods and 5 is the Rails JSON generator. It is now a
-bit faster than the generator_safe and generator_pretty methods of the pure
+In the table above 1-3 are `JSON::Ext::Generator` methods. 4, 6, and 7 are
+`JSON::Pure::Generator` methods and 5 is the Rails JSON generator. It is now a
+bit faster than the `generator_safe` and `generator_pretty` methods of the pure
 variant but slower than the others.
 
-To achieve the fastest JSON document output, you can use the fast_generate
+To achieve the fastest JSON document output, you can use the `fast_generate`
 method. Beware, that this will disable the checking for circular Ruby data
 structures, which may cause JSON to go into an infinite loop.
 
 Here are the median comparisons for completeness' sake:
 
+```
  Comparing times (call_time_median):
   1 GeneratorBenchmarkExt#generator_fast    1000 repeats:
-        708.258020939 (  real) ->   16.547x 
+        708.258020939 (  real) ->   16.547x
           0.001411915
   2 GeneratorBenchmarkExt#generator_safe    1000 repeats:
-        569.105020353 (  real) ->   13.296x 
+        569.105020353 (  real) ->   13.296x
           0.001757145
   3 GeneratorBenchmarkExt#generator_pretty  900 repeats:
-        482.825371244 (  real) ->   11.280x 
+        482.825371244 (  real) ->   11.280x
           0.002071142
   4 GeneratorBenchmarkPure#generator_fast   1000 repeats:
-         62.717626652 (  real) ->    1.465x 
+         62.717626652 (  real) ->    1.465x
           0.015944481
   5 GeneratorBenchmarkRails#generator       1000 repeats:
-         43.965681162 (  real) ->    1.027x 
+         43.965681162 (  real) ->    1.027x
           0.022745013
   6 GeneratorBenchmarkPure#generator_safe   1000 repeats:
          43.929073409 (  real) ->    1.026x 7 (>=3859)
@@ -336,22 +371,21 @@ Here are the median comparisons for completeness' sake:
           0.023363113
             calls/sec (  time) ->    speed  covers
             secs/call
+```
 
-== Author
+## Author
 
 Florian Frank <mailto:flori at ping.de>
 
-== License
+## License
 
-Ruby License, see the COPYING file included in the source distribution. The
-Ruby License includes the GNU General Public License (GPL), Version 2, so see
-the file GPL as well.
+Ruby License, see https://www.ruby-lang.org/en/about/license.txt.
 
-== Download
+## Download
 
 The latest version of this library can be downloaded at
 
-* http://rubyforge.org/frs?group_id=953
+* https://rubygems.org/gems/json
 
 Online Documentation should be located at
 
diff --git a/Rakefile b/Rakefile
index 1d13c2c..c6c195f 100644
--- a/Rakefile
+++ b/Rakefile
@@ -23,10 +23,6 @@ class UndocumentedTestTask < Rake::TestTask
   def desc(*) end
 end
 
-def skip_sdoc(src)
-  src.gsub(/^.*sdoc.*/) { |s| s + ' if RUBY_VERSION > "1.8.6"' }
-end
-
 MAKE   = ENV['MAKE']   || %w[gmake make].find { |c| system(c, '-v') }
 BUNDLE = ENV['BUNDLE'] || %w[bundle].find { |c| system(c, '-v') }
 PKG_NAME          = 'json'
@@ -87,25 +83,25 @@ if defined?(Gem) and defined?(Gem::PackageTask)
     s.files = PKG_FILES
 
     s.require_path = 'lib'
-    s.add_development_dependency 'permutation'
-    s.add_development_dependency 'sdoc', '~>0.3.16'
-    s.add_development_dependency 'rake', '~>0.9.2'
+    s.add_development_dependency 'rake'
+    s.add_development_dependency 'test-unit', '~> 2.0'
 
-    s.extra_rdoc_files << 'README.rdoc'
+    s.extra_rdoc_files << 'README.md'
     s.rdoc_options <<
-      '--title' <<  'JSON implemention for ruby' << '--main' << 'README.rdoc'
+      '--title' <<  'JSON implemention for ruby' << '--main' << 'README.md'
     s.test_files.concat Dir['./tests/test_*.rb']
 
     s.author = "Florian Frank"
     s.email = "flori at ping.de"
     s.homepage = "http://flori.github.com/#{PKG_NAME}"
     s.license = 'Ruby'
+    s.required_ruby_version = '>= 1.9'
   end
 
   desc 'Creates a json_pure.gemspec file'
   task :gemspec_pure => :version do
     File.open('json_pure.gemspec', 'w') do |gemspec|
-      gemspec.write skip_sdoc(spec_pure.to_ruby)
+      gemspec.write spec_pure.to_ruby
     end
   end
 
@@ -125,24 +121,25 @@ if defined?(Gem) and defined?(Gem::PackageTask)
     s.extensions = FileList['ext/**/extconf.rb']
 
     s.require_path = 'lib'
-    s.add_development_dependency 'permutation'
-    s.add_development_dependency 'sdoc', '~>0.3.16'
+    s.add_development_dependency 'rake'
+    s.add_development_dependency 'test-unit', '~> 2.0'
 
-    s.extra_rdoc_files << 'README.rdoc'
+    s.extra_rdoc_files << 'README.md'
     s.rdoc_options <<
-      '--title' <<  'JSON implemention for Ruby' << '--main' << 'README.rdoc'
+      '--title' <<  'JSON implemention for Ruby' << '--main' << 'README.md'
     s.test_files.concat Dir['./tests/test_*.rb']
 
     s.author = "Florian Frank"
     s.email = "flori at ping.de"
     s.homepage = "http://flori.github.com/#{PKG_NAME}"
     s.license = 'Ruby'
+    s.required_ruby_version = '>= 1.9'
   end
 
   desc 'Creates a json.gemspec file'
   task :gemspec_ext => :version do
     File.open('json.gemspec', 'w') do |gemspec|
-      gemspec.write skip_sdoc(spec_ext.to_ruby)
+      gemspec.write spec_ext.to_ruby
     end
   end
 
@@ -161,6 +158,7 @@ task :version do
   puts m
   File.open(File.join('lib', 'json', 'version.rb'), 'w') do |v|
     v.puts <<EOT
+# frozen_string_literal: false
 module JSON
   # JSON version
   VERSION         = '#{PKG_VERSION}'
@@ -173,13 +171,17 @@ EOT
   end
 end
 
+task :check_env do
+  ENV.key?('JSON') or fail "JSON env var is required"
+end
+
 desc "Testing library (pure ruby)"
-task :test_pure => [ :clean, :do_test_pure ]
+task :test_pure => [ :clean, :check_env, :do_test_pure ]
 
 UndocumentedTestTask.new do |t|
   t.name = 'do_test_pure'
-  t.libs << 'lib'
-  t.test_files = FileList['tests/test_*.rb']
+  t.libs << 'lib' << 'tests'
+  t.test_files = FileList['tests/*_test.rb']
   t.verbose = true
   t.options = '-v'
 end
@@ -198,13 +200,11 @@ namespace :gems do
 end
 
 if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
-  if ENV.key?('JAVA_HOME')
-    warn " *** JAVA_HOME was set to #{ENV['JAVA_HOME'].inspect}"
-  elsif File.directory?(local_java = '/usr/local/java/jdk') ||
-    File.directory?(local_java = '/usr/lib/jvm/java-6-openjdk')
-  then
-    ENV['JAVA_HOME'] = local_java
-  end
+  ENV['JAVA_HOME'] ||= [
+    '/usr/local/java/jdk',
+    '/usr/lib/jvm/java-6-openjdk',
+    '/Library/Java/Home',
+  ].find { |c| File.directory?(c) }
   if ENV['JAVA_HOME']
     warn " *** JAVA_HOME is set to #{ENV['JAVA_HOME'].inspect}"
     ENV['PATH'] = ENV['PATH'].split(/:/).unshift(java_path = "#{ENV['JAVA_HOME']}/bin") * ':'
@@ -257,12 +257,12 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
   end
 
   desc "Testing library (jruby)"
-  task :test_ext => [ :create_jar, :do_test_ext ]
+  task :test_ext => [ :check_env, :create_jar, :do_test_ext ]
 
   UndocumentedTestTask.new do |t|
     t.name = 'do_test_ext'
-    t.libs << 'lib'
-    t.test_files = FileList['tests/test_*.rb']
+    t.libs << 'lib' << 'tests'
+    t.test_files = FileList['tests/*_test.rb']
     t.verbose = true
     t.options = '-v'
   end
@@ -331,21 +331,16 @@ else
   end
 
   desc "Testing library (extension)"
-  task :test_ext => [ :compile, :do_test_ext ]
+  task :test_ext => [ :check_env, :compile, :do_test_ext ]
 
   UndocumentedTestTask.new do |t|
     t.name = 'do_test_ext'
-    t.libs << 'ext' << 'lib'
-    t.test_files = FileList['tests/test_*.rb']
+    t.libs << 'ext' << 'lib' << 'tests'
+    t.test_files = FileList['tests/*_test.rb']
     t.verbose = true
     t.options = '-v'
   end
 
-  desc "Create RDOC documentation"
-  task :doc => [ :version, EXT_PARSER_SRC ] do
-    sh "sdoc -o doc -t '#{PKG_TITLE}' -m README.rdoc README.rdoc lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}"
-  end
-
   desc "Generate parser with ragel"
   task :ragel => EXT_PARSER_SRC
 
@@ -367,6 +362,7 @@ else
         sh "ragel -x parser.rl | #{RAGEL_CODEGEN} -G2"
       end
       src = File.read("parser.c").gsub(/[ \t]+$/, '')
+      src.gsub!(/^static const int (JSON_.*=.*);$/, 'enum {\1};')
       File.open("parser.c", "w") {|f| f.print src}
     end
   end
diff --git a/TODO b/TODO
deleted file mode 100644
index 8b13789..0000000
--- a/TODO
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/VERSION b/VERSION
index a7ee35a..7ec1d6d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.8.3
+2.1.0
diff --git a/ext/json/ext/fbuffer/fbuffer.h b/ext/json/ext/fbuffer/fbuffer.h
index 5a0a27c..dc8f406 100644
--- a/ext/json/ext/fbuffer/fbuffer.h
+++ b/ext/json/ext/fbuffer/fbuffer.h
@@ -12,9 +12,6 @@
 #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
diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c
index 6300c64..2bf8074 100644
--- a/ext/json/ext/generator/generator.c
+++ b/ext/json/ext/generator/generator.c
@@ -7,14 +7,20 @@ static ID i_encoding, i_encode;
 #endif
 
 static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
-             mHash, mArray, mFixnum, mBignum, mFloat, mString, mString_Extend,
+             mHash, mArray,
+#ifdef RUBY_INTEGER_UNIFICATION
+             mInteger,
+#else
+             mFixnum, mBignum,
+#endif
+             mFloat, mString, mString_Extend,
              mTrueClass, mFalseClass, mNilClass, eGeneratorError,
              eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE,
              i_SAFE_STATE_PROTOTYPE;
 
 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_quirks_mode, i_pack, i_unpack, i_create_id, i_extend, i_key_p,
+          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;
 
@@ -216,6 +222,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
             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
@@ -301,7 +308,7 @@ static char *fstrndup(const char *ptr, unsigned long len) {
   char *result;
   if (len <= 0) return NULL;
   result = ALLOC_N(char, len);
-  memccpy(result, ptr, 0, len);
+  memcpy(result, ptr, len);
   return result;
 }
 
@@ -342,6 +349,18 @@ 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(*)
  *
@@ -361,6 +380,7 @@ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
 {
     GENERATE_JSON(bignum);
 }
+#endif
 
 /*
  * call-seq: to_json(*)
@@ -622,8 +642,6 @@ static VALUE cState_configure(VALUE self, VALUE opts)
     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;
 }
 
@@ -657,7 +675,6 @@ static VALUE cState_to_h(VALUE 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));
@@ -825,6 +842,15 @@ static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
     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);
@@ -858,9 +884,9 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s
         generate_json_false(buffer, Vstate, state, obj);
     } else if (obj == Qtrue) {
         generate_json_true(buffer, Vstate, state, obj);
-    } else if (klass == rb_cFixnum) {
+    } else if (FIXNUM_P(obj)) {
         generate_json_fixnum(buffer, Vstate, state, obj);
-    } else if (klass == rb_cBignum) {
+    } 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);
@@ -871,7 +897,7 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s
     } else {
         tmp = rb_funcall(obj, i_to_s, 0);
         Check_Type(tmp, T_STRING);
-        generate_json(buffer, Vstate, state, tmp);
+        generate_json_string(buffer, Vstate, state, tmp);
     }
 }
 
@@ -915,21 +941,6 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj)
 }
 
 /*
- * 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((unsigned char)*p); p++);
-    for (; q > p && isspace((unsigned char)*q); q--);
-    return (*p == '[' && *q == ']') || (*p == '{' && *q == '}');
-}
-
-/*
  * call-seq: generate(obj)
  *
  * Generates a valid JSON document from object +obj+ and returns the
@@ -940,9 +951,7 @@ static VALUE cState_generate(VALUE self, VALUE obj)
 {
     VALUE result = cState_partial_generate(self, obj);
     GET_STATE(self);
-    if (!state->quirks_mode && !isArrayOrObject(result)) {
-        rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed");
-    }
+    (void)state;
     return result;
 }
 
@@ -961,8 +970,6 @@ static VALUE cState_generate(VALUE self, VALUE obj)
  * * *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.
  */
@@ -1055,7 +1062,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
         }
     } else {
         if (state->indent) ruby_xfree(state->indent);
-        state->indent = strdup(RSTRING_PTR(indent));
+        state->indent = fstrndup(RSTRING_PTR(indent), len);
         state->indent_len = len;
     }
     return Qnil;
@@ -1093,7 +1100,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
         }
     } else {
         if (state->space) ruby_xfree(state->space);
-        state->space = strdup(RSTRING_PTR(space));
+        state->space = fstrndup(RSTRING_PTR(space), len);
         state->space_len = len;
     }
     return Qnil;
@@ -1129,7 +1136,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
         }
     } else {
         if (state->space_before) ruby_xfree(state->space_before);
-        state->space_before = strdup(RSTRING_PTR(space_before));
+        state->space_before = fstrndup(RSTRING_PTR(space_before), len);
         state->space_before_len = len;
     }
     return Qnil;
@@ -1166,7 +1173,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
         }
     } else {
         if (state->object_nl) ruby_xfree(state->object_nl);
-        state->object_nl = strdup(RSTRING_PTR(object_nl));
+        state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
         state->object_nl_len = len;
     }
     return Qnil;
@@ -1201,7 +1208,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
         }
     } else {
         if (state->array_nl) ruby_xfree(state->array_nl);
-        state->array_nl = strdup(RSTRING_PTR(array_nl));
+        state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
         state->array_nl_len = len;
     }
     return Qnil;
@@ -1270,29 +1277,6 @@ static VALUE cState_ascii_only_p(VALUE 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.
@@ -1380,9 +1364,6 @@ void Init_generator(void)
     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);
@@ -1402,10 +1383,15 @@ void Init_generator(void)
     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");
@@ -1434,7 +1420,6 @@ void Init_generator(void)
     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");
diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h
index 298c0a4..c367a62 100644
--- a/ext/json/ext/generator/generator.h
+++ b/ext/json/ext/generator/generator.h
@@ -1,7 +1,6 @@
 #ifndef _GENERATOR_H_
 #define _GENERATOR_H_
 
-#include <string.h>
 #include <math.h>
 #include <ctype.h>
 
@@ -73,7 +72,6 @@ typedef struct JSON_Generator_StateStruct {
     long max_nesting;
     char allow_nan;
     char ascii_only;
-    char quirks_mode;
     long depth;
     long buffer_initial_length;
 } JSON_Generator_State;
@@ -99,8 +97,12 @@ typedef struct JSON_Generator_StateStruct {
 
 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);
@@ -122,6 +124,9 @@ static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
 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);
diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/parser/extconf.rb
index ae4f861..f7360d4 100644
--- a/ext/json/ext/parser/extconf.rb
+++ b/ext/json/ext/parser/extconf.rb
@@ -1,3 +1,6 @@
+# frozen_string_literal: false
 require 'mkmf'
 
+have_func("rb_enc_raise", "ruby.h")
+
 create_makefile 'json/ext/parser'
diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c
index 9c9d76a..d2e4eb6 100644
--- a/ext/json/ext/parser/parser.c
+++ b/ext/json/ext/parser/parser.c
@@ -3,6 +3,28 @@
 #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 char digit_values[256] = {
@@ -67,28 +89,21 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
     return len;
 }
 
-#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;
-#else
-static ID i_iconv;
-#endif
-
 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_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;
+          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;
 
 
-#line 110 "parser.rl"
+#line 125 "parser.rl"
 
 
 
-#line 92 "parser.c"
+#line 107 "parser.c"
 enum {JSON_object_start = 1};
 enum {JSON_object_first_final = 27};
 enum {JSON_object_error = 0};
@@ -96,30 +111,30 @@ enum {JSON_object_error = 0};
 enum {JSON_object_en_main = 1};
 
 
-#line 151 "parser.rl"
+#line 166 "parser.rl"
 
 
-static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
+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 && json->current_nesting > json->max_nesting) {
-        rb_raise(eNestingError, "nesting of %d is too deep", json->current_nesting);
+    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);
 
 
-#line 116 "parser.c"
+#line 131 "parser.c"
 	{
 	cs = JSON_object_start;
 	}
 
-#line 166 "parser.rl"
+#line 181 "parser.rl"
 
-#line 123 "parser.c"
+#line 138 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -147,7 +162,7 @@ case 2:
 		goto st2;
 	goto st0;
 tr2:
-#line 133 "parser.rl"
+#line 148 "parser.rl"
 	{
         char *np;
         json->parsing_name = 1;
@@ -160,7 +175,7 @@ st3:
 	if ( ++p == pe )
 		goto _test_eof3;
 case 3:
-#line 164 "parser.c"
+#line 179 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st3;
 		case 32: goto st3;
@@ -227,10 +242,10 @@ case 8:
 		goto st8;
 	goto st0;
 tr11:
-#line 118 "parser.rl"
+#line 133 "parser.rl"
 	{
         VALUE v = Qnil;
-        char *np = JSON_parse_value(json, p, pe, &v);
+        char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
         if (np == NULL) {
             p--; {p++; cs = 9; goto _out;}
         } else {
@@ -247,7 +262,7 @@ st9:
 	if ( ++p == pe )
 		goto _test_eof9;
 case 9:
-#line 251 "parser.c"
+#line 266 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st9;
 		case 32: goto st9;
@@ -336,14 +351,14 @@ case 18:
 		goto st9;
 	goto st18;
 tr4:
-#line 141 "parser.rl"
+#line 156 "parser.rl"
 	{ p--; {p++; cs = 27; goto _out;} }
 	goto st27;
 st27:
 	if ( ++p == pe )
 		goto _test_eof27;
 case 27:
-#line 347 "parser.c"
+#line 362 "parser.c"
 	goto st0;
 st19:
 	if ( ++p == pe )
@@ -441,7 +456,7 @@ case 26:
 	_out: {}
 	}
 
-#line 167 "parser.rl"
+#line 182 "parser.rl"
 
     if (cs >= JSON_object_first_final) {
         if (json->create_additions) {
@@ -466,281 +481,358 @@ case 26:
 
 
 
-#line 470 "parser.c"
+#line 485 "parser.c"
 enum {JSON_value_start = 1};
-enum {JSON_value_first_final = 21};
+enum {JSON_value_first_final = 29};
 enum {JSON_value_error = 0};
 
 enum {JSON_value_en_main = 1};
 
 
-#line 271 "parser.rl"
+#line 282 "parser.rl"
 
 
-static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
+static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
 {
     int cs = EVIL;
 
 
-#line 486 "parser.c"
+#line 501 "parser.c"
 	{
 	cs = JSON_value_start;
 	}
 
-#line 278 "parser.rl"
+#line 289 "parser.rl"
 
-#line 493 "parser.c"
+#line 508 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
 	switch ( cs )
 	{
+st1:
+	if ( ++p == pe )
+		goto _test_eof1;
 case 1:
 	switch( (*p) ) {
-		case 34: goto tr0;
-		case 45: goto tr2;
-		case 73: goto st2;
-		case 78: goto st9;
-		case 91: goto tr5;
-		case 102: goto st11;
-		case 110: goto st15;
-		case 116: goto st18;
-		case 123: goto tr9;
+		case 13: goto st1;
+		case 32: goto st1;
+		case 34: goto tr2;
+		case 45: goto tr3;
+		case 47: goto st6;
+		case 73: goto st10;
+		case 78: goto st17;
+		case 91: goto tr7;
+		case 102: goto st19;
+		case 110: goto st23;
+		case 116: goto st26;
+		case 123: goto tr11;
 	}
-	if ( 48 <= (*p) && (*p) <= 57 )
-		goto tr2;
+	if ( (*p) > 10 ) {
+		if ( 48 <= (*p) && (*p) <= 57 )
+			goto tr3;
+	} else if ( (*p) >= 9 )
+		goto st1;
 	goto st0;
 st0:
 cs = 0;
 	goto _out;
-tr0:
-#line 219 "parser.rl"
+tr2:
+#line 234 "parser.rl"
 	{
         char *np = JSON_parse_string(json, p, pe, result);
-        if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
+        if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
     }
-	goto st21;
-tr2:
-#line 224 "parser.rl"
+	goto st29;
+tr3:
+#line 239 "parser.rl"
 	{
         char *np;
-        if(pe > p + 9 - json->quirks_mode && !strncmp(MinusInfinity, p, 9)) {
+        if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
             if (json->allow_nan) {
                 *result = CMinusInfinity;
                 {p = (( p + 10))-1;}
-                p--; {p++; cs = 21; goto _out;}
+                p--; {p++; cs = 29; goto _out;}
             } else {
-                rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+                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++; cs = 21; goto _out;}
+        p--; {p++; cs = 29; goto _out;}
     }
-	goto st21;
-tr5:
-#line 242 "parser.rl"
+	goto st29;
+tr7:
+#line 257 "parser.rl"
 	{
         char *np;
-        json->current_nesting++;
-        np = JSON_parse_array(json, p, pe, result);
-        json->current_nesting--;
-        if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
+        np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
+        if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
     }
-	goto st21;
-tr9:
-#line 250 "parser.rl"
+	goto st29;
+tr11:
+#line 263 "parser.rl"
 	{
         char *np;
-        json->current_nesting++;
-        np =  JSON_parse_object(json, p, pe, result);
-        json->current_nesting--;
-        if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
+        np =  JSON_parse_object(json, p, pe, result, current_nesting + 1);
+        if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
     }
-	goto st21;
-tr16:
-#line 212 "parser.rl"
+	goto st29;
+tr25:
+#line 227 "parser.rl"
 	{
         if (json->allow_nan) {
             *result = CInfinity;
         } else {
-            rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
+            rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
         }
     }
-	goto st21;
-tr18:
-#line 205 "parser.rl"
+	goto st29;
+tr27:
+#line 220 "parser.rl"
 	{
         if (json->allow_nan) {
             *result = CNaN;
         } else {
-            rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
+            rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
         }
     }
-	goto st21;
-tr22:
-#line 199 "parser.rl"
+	goto st29;
+tr31:
+#line 214 "parser.rl"
 	{
         *result = Qfalse;
     }
-	goto st21;
-tr25:
-#line 196 "parser.rl"
+	goto st29;
+tr34:
+#line 211 "parser.rl"
 	{
         *result = Qnil;
     }
-	goto st21;
-tr28:
-#line 202 "parser.rl"
+	goto st29;
+tr37:
+#line 217 "parser.rl"
 	{
         *result = Qtrue;
     }
-	goto st21;
-st21:
-	if ( ++p == pe )
-		goto _test_eof21;
-case 21:
-#line 258 "parser.rl"
-	{ p--; {p++; cs = 21; goto _out;} }
-#line 608 "parser.c"
+	goto st29;
+st29:
+	if ( ++p == pe )
+		goto _test_eof29;
+case 29:
+#line 269 "parser.rl"
+	{ p--; {p++; cs = 29; goto _out;} }
+#line 628 "parser.c"
+	switch( (*p) ) {
+		case 13: goto st29;
+		case 32: goto st29;
+		case 47: goto st2;
+	}
+	if ( 9 <= (*p) && (*p) <= 10 )
+		goto st29;
 	goto st0;
 st2:
 	if ( ++p == pe )
 		goto _test_eof2;
 case 2:
-	if ( (*p) == 110 )
-		goto st3;
+	switch( (*p) ) {
+		case 42: goto st3;
+		case 47: goto st5;
+	}
 	goto st0;
 st3:
 	if ( ++p == pe )
 		goto _test_eof3;
 case 3:
-	if ( (*p) == 102 )
+	if ( (*p) == 42 )
 		goto st4;
-	goto st0;
+	goto st3;
 st4:
 	if ( ++p == pe )
 		goto _test_eof4;
 case 4:
-	if ( (*p) == 105 )
-		goto st5;
-	goto st0;
+	switch( (*p) ) {
+		case 42: goto st4;
+		case 47: goto st29;
+	}
+	goto st3;
 st5:
 	if ( ++p == pe )
 		goto _test_eof5;
 case 5:
-	if ( (*p) == 110 )
-		goto st6;
-	goto st0;
+	if ( (*p) == 10 )
+		goto st29;
+	goto st5;
 st6:
 	if ( ++p == pe )
 		goto _test_eof6;
 case 6:
-	if ( (*p) == 105 )
-		goto st7;
+	switch( (*p) ) {
+		case 42: goto st7;
+		case 47: goto st9;
+	}
 	goto st0;
 st7:
 	if ( ++p == pe )
 		goto _test_eof7;
 case 7:
-	if ( (*p) == 116 )
+	if ( (*p) == 42 )
 		goto st8;
-	goto st0;
+	goto st7;
 st8:
 	if ( ++p == pe )
 		goto _test_eof8;
 case 8:
-	if ( (*p) == 121 )
-		goto tr16;
-	goto st0;
+	switch( (*p) ) {
+		case 42: goto st8;
+		case 47: goto st1;
+	}
+	goto st7;
 st9:
 	if ( ++p == pe )
 		goto _test_eof9;
 case 9:
-	if ( (*p) == 97 )
-		goto st10;
-	goto st0;
+	if ( (*p) == 10 )
+		goto st1;
+	goto st9;
 st10:
 	if ( ++p == pe )
 		goto _test_eof10;
 case 10:
-	if ( (*p) == 78 )
-		goto tr18;
+	if ( (*p) == 110 )
+		goto st11;
 	goto st0;
 st11:
 	if ( ++p == pe )
 		goto _test_eof11;
 case 11:
-	if ( (*p) == 97 )
+	if ( (*p) == 102 )
 		goto st12;
 	goto st0;
 st12:
 	if ( ++p == pe )
 		goto _test_eof12;
 case 12:
-	if ( (*p) == 108 )
+	if ( (*p) == 105 )
 		goto st13;
 	goto st0;
 st13:
 	if ( ++p == pe )
 		goto _test_eof13;
 case 13:
-	if ( (*p) == 115 )
+	if ( (*p) == 110 )
 		goto st14;
 	goto st0;
 st14:
 	if ( ++p == pe )
 		goto _test_eof14;
 case 14:
-	if ( (*p) == 101 )
-		goto tr22;
+	if ( (*p) == 105 )
+		goto st15;
 	goto st0;
 st15:
 	if ( ++p == pe )
 		goto _test_eof15;
 case 15:
-	if ( (*p) == 117 )
+	if ( (*p) == 116 )
 		goto st16;
 	goto st0;
 st16:
 	if ( ++p == pe )
 		goto _test_eof16;
 case 16:
-	if ( (*p) == 108 )
-		goto st17;
+	if ( (*p) == 121 )
+		goto tr25;
 	goto st0;
 st17:
 	if ( ++p == pe )
 		goto _test_eof17;
 case 17:
-	if ( (*p) == 108 )
-		goto tr25;
+	if ( (*p) == 97 )
+		goto st18;
 	goto st0;
 st18:
 	if ( ++p == pe )
 		goto _test_eof18;
 case 18:
-	if ( (*p) == 114 )
-		goto st19;
+	if ( (*p) == 78 )
+		goto tr27;
 	goto st0;
 st19:
 	if ( ++p == pe )
 		goto _test_eof19;
 case 19:
-	if ( (*p) == 117 )
+	if ( (*p) == 97 )
 		goto st20;
 	goto st0;
 st20:
 	if ( ++p == pe )
 		goto _test_eof20;
 case 20:
+	if ( (*p) == 108 )
+		goto st21;
+	goto st0;
+st21:
+	if ( ++p == pe )
+		goto _test_eof21;
+case 21:
+	if ( (*p) == 115 )
+		goto st22;
+	goto st0;
+st22:
+	if ( ++p == pe )
+		goto _test_eof22;
+case 22:
+	if ( (*p) == 101 )
+		goto tr31;
+	goto st0;
+st23:
+	if ( ++p == pe )
+		goto _test_eof23;
+case 23:
+	if ( (*p) == 117 )
+		goto st24;
+	goto st0;
+st24:
+	if ( ++p == pe )
+		goto _test_eof24;
+case 24:
+	if ( (*p) == 108 )
+		goto st25;
+	goto st0;
+st25:
+	if ( ++p == pe )
+		goto _test_eof25;
+case 25:
+	if ( (*p) == 108 )
+		goto tr34;
+	goto st0;
+st26:
+	if ( ++p == pe )
+		goto _test_eof26;
+case 26:
+	if ( (*p) == 114 )
+		goto st27;
+	goto st0;
+st27:
+	if ( ++p == pe )
+		goto _test_eof27;
+case 27:
+	if ( (*p) == 117 )
+		goto st28;
+	goto st0;
+st28:
+	if ( ++p == pe )
+		goto _test_eof28;
+case 28:
 	if ( (*p) == 101 )
-		goto tr28;
+		goto tr37;
 	goto st0;
 	}
-	_test_eof21: cs = 21; goto _test_eof;
+	_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;
@@ -760,12 +852,20 @@ case 20:
 	_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 279 "parser.rl"
+#line 290 "parser.rl"
 
     if (cs >= JSON_value_first_final) {
         return p;
@@ -775,7 +875,7 @@ case 20:
 }
 
 
-#line 779 "parser.c"
+#line 879 "parser.c"
 enum {JSON_integer_start = 1};
 enum {JSON_integer_first_final = 3};
 enum {JSON_integer_error = 0};
@@ -783,7 +883,7 @@ enum {JSON_integer_error = 0};
 enum {JSON_integer_en_main = 1};
 
 
-#line 295 "parser.rl"
+#line 306 "parser.rl"
 
 
 static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -791,15 +891,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
     int cs = EVIL;
 
 
-#line 795 "parser.c"
+#line 895 "parser.c"
 	{
 	cs = JSON_integer_start;
 	}
 
-#line 302 "parser.rl"
+#line 313 "parser.rl"
     json->memo = p;
 
-#line 803 "parser.c"
+#line 903 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -833,14 +933,14 @@ case 3:
 		goto st0;
 	goto tr4;
 tr4:
-#line 292 "parser.rl"
+#line 303 "parser.rl"
 	{ p--; {p++; cs = 4; goto _out;} }
 	goto st4;
 st4:
 	if ( ++p == pe )
 		goto _test_eof4;
 case 4:
-#line 844 "parser.c"
+#line 944 "parser.c"
 	goto st0;
 st5:
 	if ( ++p == pe )
@@ -859,7 +959,7 @@ case 5:
 	_out: {}
 	}
 
-#line 304 "parser.rl"
+#line 315 "parser.rl"
 
     if (cs >= JSON_integer_first_final) {
         long len = p - json->memo;
@@ -874,7 +974,7 @@ case 5:
 }
 
 
-#line 878 "parser.c"
+#line 978 "parser.c"
 enum {JSON_float_start = 1};
 enum {JSON_float_first_final = 8};
 enum {JSON_float_error = 0};
@@ -882,7 +982,7 @@ enum {JSON_float_error = 0};
 enum {JSON_float_en_main = 1};
 
 
-#line 329 "parser.rl"
+#line 340 "parser.rl"
 
 
 static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -890,15 +990,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
     int cs = EVIL;
 
 
-#line 894 "parser.c"
+#line 994 "parser.c"
 	{
 	cs = JSON_float_start;
 	}
 
-#line 336 "parser.rl"
+#line 347 "parser.rl"
     json->memo = p;
 
-#line 902 "parser.c"
+#line 1002 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -956,14 +1056,14 @@ case 8:
 		goto st0;
 	goto tr9;
 tr9:
-#line 323 "parser.rl"
+#line 334 "parser.rl"
 	{ p--; {p++; cs = 9; goto _out;} }
 	goto st9;
 st9:
 	if ( ++p == pe )
 		goto _test_eof9;
 case 9:
-#line 967 "parser.c"
+#line 1067 "parser.c"
 	goto st0;
 st5:
 	if ( ++p == pe )
@@ -1024,14 +1124,20 @@ case 7:
 	_out: {}
 	}
 
-#line 338 "parser.rl"
+#line 349 "parser.rl"
 
     if (cs >= JSON_float_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_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        if (NIL_P(json->decimal_class)) {
+          *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        } else {
+          VALUE text;
+          text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+          *result = rb_funcall(json->decimal_class, i_new, 1, text);
+        }
         return p + 1;
     } else {
         return NULL;
@@ -1040,7 +1146,7 @@ case 7:
 
 
 
-#line 1044 "parser.c"
+#line 1150 "parser.c"
 enum {JSON_array_start = 1};
 enum {JSON_array_first_final = 17};
 enum {JSON_array_error = 0};
@@ -1048,28 +1154,28 @@ enum {JSON_array_error = 0};
 enum {JSON_array_en_main = 1};
 
 
-#line 381 "parser.rl"
+#line 398 "parser.rl"
 
 
-static char *JSON_parse_array(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)
 {
     int cs = EVIL;
     VALUE array_class = json->array_class;
 
-    if (json->max_nesting && json->current_nesting > json->max_nesting) {
-        rb_raise(eNestingError, "nesting of %d is too deep", json->current_nesting);
+    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);
 
 
-#line 1066 "parser.c"
+#line 1172 "parser.c"
 	{
 	cs = JSON_array_start;
 	}
 
-#line 394 "parser.rl"
+#line 411 "parser.rl"
 
-#line 1073 "parser.c"
+#line 1179 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -1108,10 +1214,10 @@ case 2:
 		goto st2;
 	goto st0;
 tr2:
-#line 358 "parser.rl"
+#line 375 "parser.rl"
 	{
         VALUE v = Qnil;
-        char *np = JSON_parse_value(json, p, pe, &v);
+        char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
         if (np == NULL) {
             p--; {p++; cs = 3; goto _out;}
         } else {
@@ -1128,7 +1234,7 @@ st3:
 	if ( ++p == pe )
 		goto _test_eof3;
 case 3:
-#line 1132 "parser.c"
+#line 1238 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st3;
 		case 32: goto st3;
@@ -1228,14 +1334,14 @@ case 12:
 		goto st3;
 	goto st12;
 tr4:
-#line 373 "parser.rl"
+#line 390 "parser.rl"
 	{ p--; {p++; cs = 17; goto _out;} }
 	goto st17;
 st17:
 	if ( ++p == pe )
 		goto _test_eof17;
 case 17:
-#line 1239 "parser.c"
+#line 1345 "parser.c"
 	goto st0;
 st13:
 	if ( ++p == pe )
@@ -1291,12 +1397,12 @@ case 16:
 	_out: {}
 	}
 
-#line 395 "parser.rl"
+#line 412 "parser.rl"
 
     if(cs >= JSON_array_first_final) {
         return p + 1;
     } else {
-        rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+        rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
         return NULL;
     }
 }
@@ -1336,13 +1442,21 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
                     break;
                 case 'u':
                     if (pe > stringEnd - 4) {
-                        return Qnil;
+                      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) return Qnil;
+                            if (pe > stringEnd - 6) {
+                              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)
@@ -1372,7 +1486,7 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
 }
 
 
-#line 1376 "parser.c"
+#line 1490 "parser.c"
 enum {JSON_string_start = 1};
 enum {JSON_string_first_final = 8};
 enum {JSON_string_error = 0};
@@ -1380,7 +1494,7 @@ enum {JSON_string_error = 0};
 enum {JSON_string_en_main = 1};
 
 
-#line 494 "parser.rl"
+#line 519 "parser.rl"
 
 
 static int
@@ -1402,15 +1516,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
 
     *result = rb_str_buf_new(0);
 
-#line 1406 "parser.c"
+#line 1520 "parser.c"
 	{
 	cs = JSON_string_start;
 	}
 
-#line 515 "parser.rl"
+#line 540 "parser.rl"
     json->memo = p;
 
-#line 1414 "parser.c"
+#line 1528 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -1435,7 +1549,7 @@ case 2:
 		goto st0;
 	goto st2;
 tr2:
-#line 480 "parser.rl"
+#line 505 "parser.rl"
 	{
         *result = json_string_unescape(*result, json->memo + 1, p);
         if (NIL_P(*result)) {
@@ -1446,14 +1560,14 @@ tr2:
             {p = (( p + 1))-1;}
         }
     }
-#line 491 "parser.rl"
+#line 516 "parser.rl"
 	{ p--; {p++; cs = 8; goto _out;} }
 	goto st8;
 st8:
 	if ( ++p == pe )
 		goto _test_eof8;
 case 8:
-#line 1457 "parser.c"
+#line 1571 "parser.c"
 	goto st0;
 st3:
 	if ( ++p == pe )
@@ -1529,7 +1643,7 @@ case 7:
 	_out: {}
 	}
 
-#line 517 "parser.rl"
+#line 542 "parser.rl"
 
     if (json->create_additions && RTEST(match_string = json->match_string)) {
           VALUE klass;
@@ -1544,6 +1658,8 @@ case 7:
 
     if (json->symbolize_names && json->parsing_name) {
       *result = rb_str_intern(*result);
+    } else {
+      rb_str_resize(*result, RSTRING_LEN(*result));
     }
     if (cs >= JSON_string_first_final) {
         return p + 1;
@@ -1566,41 +1682,16 @@ case 7:
 
 static VALUE convert_encoding(VALUE source)
 {
-    char *ptr = RSTRING_PTR(source);
-    long len = RSTRING_LEN(source);
-    if (len < 2) {
-        rb_raise(eParserError, "A JSON text must at least contain two octets!");
-    }
 #ifdef HAVE_RUBY_ENCODING_H
-    {
-        VALUE encoding = rb_funcall(source, i_encoding, 0);
-        if (encoding == CEncoding_ASCII_8BIT) {
-            if (len >= 4 &&  ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32BE);
-            } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16BE);
-            } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32LE);
-            } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16LE);
-            } else {
-                source = rb_str_dup(source);
-                FORCE_UTF8(source);
-            }
-        } else {
-            source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8);
-        }
-    }
-#else
-    if (len >= 4 &&  ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32be"), source);
-    } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16be"), source);
-    } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32le"), source);
-    } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16le"), source);
+  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;
 }
@@ -1623,8 +1714,9 @@ static VALUE convert_encoding(VALUE source)
  *   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.
+ *   (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.
@@ -1639,12 +1731,18 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
     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);
@@ -1669,19 +1767,17 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } else {
                 json->symbolize_names = 0;
             }
-            tmp = ID2SYM(i_quirks_mode);
-            if (option_given_p(opts, tmp)) {
-                VALUE quirks_mode = rb_hash_aref(opts, tmp);
-                json->quirks_mode = RTEST(quirks_mode) ? 1 : 0;
-            } else {
-                json->quirks_mode = 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);
@@ -1700,6 +1796,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } 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);
@@ -1707,7 +1809,9 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } else {
                 json->match_string = Qnil;
             }
+#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
         }
+#endif
     } else {
         json->max_nesting = 100;
         json->allow_nan = 0;
@@ -1715,12 +1819,9 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
         json->create_id = rb_funcall(mJSON, i_create_id, 0);
         json->object_class = Qnil;
         json->array_class = Qnil;
+        json->decimal_class = Qnil;
     }
-    source = rb_convert_type(source, T_STRING, "String", "to_str");
-    if (!json->quirks_mode) {
-      source = convert_encoding(StringValue(source));
-    }
-    json->current_nesting = 0;
+    source = convert_encoding(StringValue(source));
     StringValue(source);
     json->len = RSTRING_LEN(source);
     json->source = RSTRING_PTR(source);;
@@ -1729,7 +1830,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
 }
 
 
-#line 1733 "parser.c"
+#line 1834 "parser.c"
 enum {JSON_start = 1};
 enum {JSON_first_final = 10};
 enum {JSON_error = 0};
@@ -1737,201 +1838,33 @@ enum {JSON_error = 0};
 enum {JSON_en_main = 1};
 
 
-#line 740 "parser.rl"
+#line 742 "parser.rl"
 
 
-static VALUE cParser_parse_strict(VALUE self)
+/*
+ * 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;
+  char *p, *pe;
+  int cs = EVIL;
+  VALUE result = Qnil;
+  GET_PARSER;
 
 
-#line 1752 "parser.c"
+#line 1859 "parser.c"
 	{
 	cs = JSON_start;
 	}
 
-#line 750 "parser.rl"
-    p = json->source;
-    pe = p + json->len;
-
-#line 1761 "parser.c"
-	{
-	if ( p == pe )
-		goto _test_eof;
-	switch ( cs )
-	{
-st1:
-	if ( ++p == pe )
-		goto _test_eof1;
-case 1:
-	switch( (*p) ) {
-		case 13: goto st1;
-		case 32: goto st1;
-		case 47: goto st2;
-		case 91: goto tr3;
-		case 123: goto tr4;
-	}
-	if ( 9 <= (*p) && (*p) <= 10 )
-		goto st1;
-	goto st0;
-st0:
-cs = 0;
-	goto _out;
-st2:
-	if ( ++p == pe )
-		goto _test_eof2;
-case 2:
-	switch( (*p) ) {
-		case 42: goto st3;
-		case 47: goto st5;
-	}
-	goto st0;
-st3:
-	if ( ++p == pe )
-		goto _test_eof3;
-case 3:
-	if ( (*p) == 42 )
-		goto st4;
-	goto st3;
-st4:
-	if ( ++p == pe )
-		goto _test_eof4;
-case 4:
-	switch( (*p) ) {
-		case 42: goto st4;
-		case 47: goto st1;
-	}
-	goto st3;
-st5:
-	if ( ++p == pe )
-		goto _test_eof5;
-case 5:
-	if ( (*p) == 10 )
-		goto st1;
-	goto st5;
-tr3:
-#line 729 "parser.rl"
-	{
-        char *np;
-        json->current_nesting = 1;
-        np = JSON_parse_array(json, p, pe, &result);
-        if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
-    }
-	goto st10;
-tr4:
-#line 722 "parser.rl"
-	{
-        char *np;
-        json->current_nesting = 1;
-        np = JSON_parse_object(json, p, pe, &result);
-        if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
-    }
-	goto st10;
-st10:
-	if ( ++p == pe )
-		goto _test_eof10;
-case 10:
-#line 1838 "parser.c"
-	switch( (*p) ) {
-		case 13: goto st10;
-		case 32: goto st10;
-		case 47: goto st6;
-	}
-	if ( 9 <= (*p) && (*p) <= 10 )
-		goto st10;
-	goto st0;
-st6:
-	if ( ++p == pe )
-		goto _test_eof6;
-case 6:
-	switch( (*p) ) {
-		case 42: goto st7;
-		case 47: goto st9;
-	}
-	goto st0;
-st7:
-	if ( ++p == pe )
-		goto _test_eof7;
-case 7:
-	if ( (*p) == 42 )
-		goto st8;
-	goto st7;
-st8:
-	if ( ++p == pe )
-		goto _test_eof8;
-case 8:
-	switch( (*p) ) {
-		case 42: goto st8;
-		case 47: goto st10;
-	}
-	goto st7;
-st9:
-	if ( ++p == pe )
-		goto _test_eof9;
-case 9:
-	if ( (*p) == 10 )
-		goto st10;
-	goto st9;
-	}
-	_test_eof1: cs = 1; 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_eof10: cs = 10; 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 753 "parser.rl"
-
-    if (cs >= JSON_first_final && p == pe) {
-        return result;
-    } else {
-        rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
-        return Qnil;
-    }
-}
-
-
-
-#line 1907 "parser.c"
-enum {JSON_quirks_mode_start = 1};
-enum {JSON_quirks_mode_first_final = 10};
-enum {JSON_quirks_mode_error = 0};
-
-enum {JSON_quirks_mode_en_main = 1};
-
-
-#line 778 "parser.rl"
-
-
-static VALUE cParser_parse_quirks_mode(VALUE self)
-{
-    char *p, *pe;
-    int cs = EVIL;
-    VALUE result = Qnil;
-    GET_PARSER;
-
-
-#line 1926 "parser.c"
-	{
-	cs = JSON_quirks_mode_start;
-	}
-
-#line 788 "parser.rl"
-    p = json->source;
-    pe = p + json->len;
+#line 758 "parser.rl"
+  p = json->source;
+  pe = p + json->len;
 
-#line 1935 "parser.c"
+#line 1868 "parser.c"
 	{
 	if ( p == pe )
 		goto _test_eof;
@@ -1965,9 +1898,9 @@ st0:
 cs = 0;
 	goto _out;
 tr2:
-#line 770 "parser.rl"
+#line 734 "parser.rl"
 	{
-        char *np = JSON_parse_value(json, p, pe, &result);
+        char *np = JSON_parse_value(json, p, pe, &result, 0);
         if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
     }
 	goto st10;
@@ -1975,7 +1908,7 @@ st10:
 	if ( ++p == pe )
 		goto _test_eof10;
 case 10:
-#line 1979 "parser.c"
+#line 1912 "parser.c"
 	switch( (*p) ) {
 		case 13: goto st10;
 		case 32: goto st10;
@@ -2064,30 +1997,13 @@ case 9:
 	_out: {}
 	}
 
-#line 791 "parser.rl"
+#line 761 "parser.rl"
 
-    if (cs >= JSON_quirks_mode_first_final && p == pe) {
-        return result;
-    } else {
-        rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
-        return Qnil;
-    }
-}
-
-/*
- * call-seq: parse()
- *
- *  Parses the current JSON text _source_ and returns the complete data
- *  structure as a result.
- */
-static VALUE cParser_parse(VALUE self)
-{
-  GET_PARSER;
-
-  if (json->quirks_mode) {
-    return cParser_parse_quirks_mode(self);
+  if (cs >= JSON_first_final && p == pe) {
+    return result;
   } else {
-    return cParser_parse_strict(self);
+    rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+    return Qnil;
   }
 }
 
@@ -2098,6 +2014,7 @@ static void JSON_mark(void *ptr)
     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);
 }
 
@@ -2145,18 +2062,6 @@ static VALUE cParser_source(VALUE self)
     return rb_str_dup(json->Vsource);
 }
 
-/*
- * call-seq: quirks_mode?()
- *
- * Returns a true, if this parser is in quirks_mode, false otherwise.
- */
-static VALUE cParser_quirks_mode_p(VALUE self)
-{
-    GET_PARSER;
-    return json->quirks_mode ? Qtrue : Qfalse;
-}
-
-
 void Init_parser(void)
 {
     rb_require("json/common");
@@ -2169,7 +2074,6 @@ void Init_parser(void)
     rb_define_method(cParser, "initialize", cParser_initialize, -1);
     rb_define_method(cParser, "parse", cParser_parse, 0);
     rb_define_method(cParser, "source", cParser_source, 0);
-    rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0);
 
     CNaN = rb_const_get(mJSON, rb_intern("NaN"));
     CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
@@ -2183,9 +2087,9 @@ void Init_parser(void)
     i_max_nesting = rb_intern("max_nesting");
     i_allow_nan = rb_intern("allow_nan");
     i_symbolize_names = rb_intern("symbolize_names");
-    i_quirks_mode = rb_intern("quirks_mode");
     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?");
@@ -2193,18 +2097,7 @@ void Init_parser(void)
     i_aset = rb_intern("[]=");
     i_aref = rb_intern("[]");
     i_leftshift = rb_intern("<<");
-#ifdef HAVE_RUBY_ENCODING_H
-    CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
-    CEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be"));
-    CEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le"));
-    CEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be"));
-    CEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le"));
-    CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit"));
-    i_encoding = rb_intern("encoding");
-    i_encode = rb_intern("encode");
-#else
-    i_iconv = rb_intern("iconv");
-#endif
+    i_new = rb_intern("new");
 }
 
 /*
diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h
index abcc257..e6cf779 100644
--- a/ext/json/ext/parser/parser.h
+++ b/ext/json/ext/parser/parser.h
@@ -34,13 +34,12 @@ typedef struct JSON_ParserStruct {
     char *memo;
     VALUE create_id;
     int max_nesting;
-    int current_nesting;
     int allow_nan;
     int parsing_name;
     int symbolize_names;
-    int quirks_mode;
     VALUE object_class;
     VALUE array_class;
+    VALUE decimal_class;
     int create_additions;
     VALUE match_string;
     FBuffer *fbuffer;
@@ -58,11 +57,11 @@ typedef struct JSON_ParserStruct {
 
 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);
-static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
+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);
+static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting);
 static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd);
 static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
 static VALUE convert_encoding(VALUE source);
diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl
index 216ad26..29900a4 100644
--- a/ext/json/ext/parser/parser.rl
+++ b/ext/json/ext/parser/parser.rl
@@ -1,6 +1,28 @@
 #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 char digit_values[256] = {
@@ -65,21 +87,14 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
     return len;
 }
 
-#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;
-#else
-static ID i_iconv;
-#endif
-
 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_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;
+          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;
 
 %%{
     machine JSON_common;
@@ -117,7 +132,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
 
     action parse_value {
         VALUE v = Qnil;
-        char *np = JSON_parse_value(json, fpc, pe, &v);
+        char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting);
         if (np == NULL) {
             fhold; fbreak;
         } else {
@@ -150,14 +165,14 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
     ) @exit;
 }%%
 
-static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
+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 && json->current_nesting > json->max_nesting) {
-        rb_raise(eNestingError, "nesting of %d is too deep", json->current_nesting);
+    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);
@@ -206,14 +221,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
         if (json->allow_nan) {
             *result = CNaN;
         } else {
-            rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 2);
+            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_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
+            rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p - 8);
         }
     }
     action parse_string {
@@ -223,13 +238,13 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
 
     action parse_number {
         char *np;
-        if(pe > fpc + 9 - json->quirks_mode && !strncmp(MinusInfinity, fpc, 9)) {
+        if(pe > fpc + 8 && !strncmp(MinusInfinity, fpc, 9)) {
             if (json->allow_nan) {
                 *result = CMinusInfinity;
                 fexec p + 10;
                 fhold; fbreak;
             } else {
-                rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+                rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
             }
         }
         np = JSON_parse_float(json, fpc, pe, result);
@@ -241,23 +256,19 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
 
     action parse_array {
         char *np;
-        json->current_nesting++;
-        np = JSON_parse_array(json, fpc, pe, result);
-        json->current_nesting--;
+        np = JSON_parse_array(json, fpc, pe, result, current_nesting + 1);
         if (np == NULL) { fhold; fbreak; } else fexec np;
     }
 
     action parse_object {
         char *np;
-        json->current_nesting++;
-        np =  JSON_parse_object(json, fpc, pe, result);
-        json->current_nesting--;
+        np =  JSON_parse_object(json, fpc, pe, result, current_nesting + 1);
         if (np == NULL) { fhold; fbreak; } else fexec np;
     }
 
     action exit { fhold; fbreak; }
 
-main := (
+main := ignore* (
               Vnull @parse_null |
               Vfalse @parse_false |
               Vtrue @parse_true |
@@ -267,10 +278,10 @@ main := (
               begin_string >parse_string |
               begin_array >parse_array |
               begin_object >parse_object
-        ) %*exit;
+        ) ignore* %*exit;
 }%%
 
-static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
+static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
 {
     int cs = EVIL;
 
@@ -341,7 +352,13 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
         fbuffer_clear(json->fbuffer);
         fbuffer_append(json->fbuffer, json->memo, len);
         fbuffer_append_char(json->fbuffer, '\0');
-        *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        if (NIL_P(json->decimal_class)) {
+          *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
+        } else {
+          VALUE text;
+          text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
+          *result = rb_funcall(json->decimal_class, i_new, 1, text);
+        }
         return p + 1;
     } else {
         return NULL;
@@ -357,7 +374,7 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
 
     action parse_value {
         VALUE v = Qnil;
-        char *np = JSON_parse_value(json, fpc, pe, &v);
+        char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting);
         if (np == NULL) {
             fhold; fbreak;
         } else {
@@ -380,13 +397,13 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
           end_array @exit;
 }%%
 
-static char *JSON_parse_array(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)
 {
     int cs = EVIL;
     VALUE array_class = json->array_class;
 
-    if (json->max_nesting && json->current_nesting > json->max_nesting) {
-        rb_raise(eNestingError, "nesting of %d is too deep", json->current_nesting);
+    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);
 
@@ -396,7 +413,7 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
     if(cs >= JSON_array_first_final) {
         return p + 1;
     } else {
-        rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+        rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
         return NULL;
     }
 }
@@ -436,13 +453,21 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
                     break;
                 case 'u':
                     if (pe > stringEnd - 4) {
-                        return Qnil;
+                      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) return Qnil;
+                            if (pe > stringEnd - 6) {
+                              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)
@@ -528,6 +553,8 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
 
     if (json->symbolize_names && json->parsing_name) {
       *result = rb_str_intern(*result);
+    } else {
+      rb_str_resize(*result, RSTRING_LEN(*result));
     }
     if (cs >= JSON_string_first_final) {
         return p + 1;
@@ -550,41 +577,16 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
 
 static VALUE convert_encoding(VALUE source)
 {
-    char *ptr = RSTRING_PTR(source);
-    long len = RSTRING_LEN(source);
-    if (len < 2) {
-        rb_raise(eParserError, "A JSON text must at least contain two octets!");
-    }
 #ifdef HAVE_RUBY_ENCODING_H
-    {
-        VALUE encoding = rb_funcall(source, i_encoding, 0);
-        if (encoding == CEncoding_ASCII_8BIT) {
-            if (len >= 4 &&  ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32BE);
-            } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16BE);
-            } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_32LE);
-            } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
-                source = rb_funcall(source, i_encode, 2, CEncoding_UTF_8, CEncoding_UTF_16LE);
-            } else {
-                source = rb_str_dup(source);
-                FORCE_UTF8(source);
-            }
-        } else {
-            source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8);
-        }
-    }
-#else
-    if (len >= 4 &&  ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32be"), source);
-    } else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16be"), source);
-    } else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-32le"), source);
-    } else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
-      source = rb_funcall(mJSON, i_iconv, 3, rb_str_new2("utf-8"), rb_str_new2("utf-16le"), source);
+  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;
 }
@@ -607,8 +609,9 @@ static VALUE convert_encoding(VALUE source)
  *   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.
+ *   (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.
@@ -623,12 +626,18 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
     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);
@@ -653,19 +662,17 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } else {
                 json->symbolize_names = 0;
             }
-            tmp = ID2SYM(i_quirks_mode);
-            if (option_given_p(opts, tmp)) {
-                VALUE quirks_mode = rb_hash_aref(opts, tmp);
-                json->quirks_mode = RTEST(quirks_mode) ? 1 : 0;
-            } else {
-                json->quirks_mode = 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);
@@ -684,6 +691,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } 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);
@@ -691,7 +704,9 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
             } else {
                 json->match_string = Qnil;
             }
+#ifndef HAVE_RB_SCAN_ARGS_OPTIONAL_HASH
         }
+#endif
     } else {
         json->max_nesting = 100;
         json->allow_nan = 0;
@@ -699,12 +714,9 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
         json->create_id = rb_funcall(mJSON, i_create_id, 0);
         json->object_class = Qnil;
         json->array_class = Qnil;
+        json->decimal_class = Qnil;
     }
-    source = rb_convert_type(source, T_STRING, "String", "to_str");
-    if (!json->quirks_mode) {
-      source = convert_encoding(StringValue(source));
-    }
-    json->current_nesting = 0;
+    source = convert_encoding(StringValue(source));
     StringValue(source);
     json->len = RSTRING_LEN(source);
     json->source = RSTRING_PTR(source);;
@@ -719,56 +731,8 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
 
     include JSON_common;
 
-    action parse_object {
-        char *np;
-        json->current_nesting = 1;
-        np = JSON_parse_object(json, fpc, pe, &result);
-        if (np == NULL) { fhold; fbreak; } else fexec np;
-    }
-
-    action parse_array {
-        char *np;
-        json->current_nesting = 1;
-        np = JSON_parse_array(json, fpc, pe, &result);
-        if (np == NULL) { fhold; fbreak; } else fexec np;
-    }
-
-    main := ignore* (
-            begin_object >parse_object |
-            begin_array >parse_array
-            ) ignore*;
-}%%
-
-static VALUE cParser_parse_strict(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_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
-        return Qnil;
-    }
-}
-
-
-%%{
-    machine JSON_quirks_mode;
-
-    write data;
-
-    include JSON_common;
-
     action parse_value {
-        char *np = JSON_parse_value(json, fpc, pe, &result);
+        char *np = JSON_parse_value(json, fpc, pe, &result, 0);
         if (np == NULL) { fhold; fbreak; } else fexec np;
     }
 
@@ -777,26 +741,6 @@ static VALUE cParser_parse_strict(VALUE self)
             ) ignore*;
 }%%
 
-static VALUE cParser_parse_quirks_mode(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_quirks_mode_first_final && p == pe) {
-        return result;
-    } else {
-        rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p);
-        return Qnil;
-    }
-}
-
 /*
  * call-seq: parse()
  *
@@ -805,12 +749,21 @@ static VALUE cParser_parse_quirks_mode(VALUE self)
  */
 static VALUE cParser_parse(VALUE self)
 {
+  char *p, *pe;
+  int cs = EVIL;
+  VALUE result = Qnil;
   GET_PARSER;
 
-  if (json->quirks_mode) {
-    return cParser_parse_quirks_mode(self);
+  %% write init;
+  p = json->source;
+  pe = p + json->len;
+  %% write exec;
+
+  if (cs >= JSON_first_final && p == pe) {
+    return result;
   } else {
-    return cParser_parse_strict(self);
+    rb_enc_raise(EXC_ENCODING eParserError, "%u: unexpected token at '%s'", __LINE__, p);
+    return Qnil;
   }
 }
 
@@ -821,6 +774,7 @@ static void JSON_mark(void *ptr)
     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);
 }
 
@@ -868,18 +822,6 @@ static VALUE cParser_source(VALUE self)
     return rb_str_dup(json->Vsource);
 }
 
-/*
- * call-seq: quirks_mode?()
- *
- * Returns a true, if this parser is in quirks_mode, false otherwise.
- */
-static VALUE cParser_quirks_mode_p(VALUE self)
-{
-    GET_PARSER;
-    return json->quirks_mode ? Qtrue : Qfalse;
-}
-
-
 void Init_parser(void)
 {
     rb_require("json/common");
@@ -892,7 +834,6 @@ void Init_parser(void)
     rb_define_method(cParser, "initialize", cParser_initialize, -1);
     rb_define_method(cParser, "parse", cParser_parse, 0);
     rb_define_method(cParser, "source", cParser_source, 0);
-    rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0);
 
     CNaN = rb_const_get(mJSON, rb_intern("NaN"));
     CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
@@ -906,9 +847,9 @@ void Init_parser(void)
     i_max_nesting = rb_intern("max_nesting");
     i_allow_nan = rb_intern("allow_nan");
     i_symbolize_names = rb_intern("symbolize_names");
-    i_quirks_mode = rb_intern("quirks_mode");
     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?");
@@ -916,18 +857,7 @@ void Init_parser(void)
     i_aset = rb_intern("[]=");
     i_aref = rb_intern("[]");
     i_leftshift = rb_intern("<<");
-#ifdef HAVE_RUBY_ENCODING_H
-    CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
-    CEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be"));
-    CEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le"));
-    CEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be"));
-    CEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le"));
-    CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit"));
-    i_encoding = rb_intern("encoding");
-    i_encode = rb_intern("encode");
-#else
-    i_iconv = rb_intern("iconv");
-#endif
+    i_new = rb_intern("new");
 }
 
 /*
diff --git a/ext/json/extconf.rb b/ext/json/extconf.rb
index 850798c..7595d58 100644
--- a/ext/json/extconf.rb
+++ b/ext/json/extconf.rb
@@ -1,3 +1,2 @@
 require 'mkmf'
 create_makefile('json')
-
diff --git a/java/src/json/ext/ByteListTranscoder.java b/java/src/json/ext/ByteListTranscoder.java
index ed9e54b..6f6ab66 100644
--- a/java/src/json/ext/ByteListTranscoder.java
+++ b/java/src/json/ext/ByteListTranscoder.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java
index ecceb27..96a5e7e 100644
--- a/java/src/json/ext/Generator.java
+++ b/java/src/json/ext/Generator.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
@@ -173,9 +172,7 @@ public final class Generator {
             result = RubyString.newString(session.getRuntime(), buffer);
             ThreadContext context = session.getContext();
             RuntimeInfo info = session.getInfo();
-            if (info.encodingsSupported()) {
-                result.force_encoding(context, info.utf8.get());
-            }
+            result.force_encoding(context, info.utf8.get());
             return result;
         }
 
@@ -382,8 +379,7 @@ public final class Generator {
                 RuntimeInfo info = session.getInfo();
                 RubyString src;
 
-                if (info.encodingsSupported() &&
-                        object.encoding(session.getContext()) != info.utf8.get()) {
+                if (object.encoding(session.getContext()) != info.utf8.get()) {
                     src = (RubyString)object.encode(session.getContext(),
                                                     info.utf8.get());
                 } else {
@@ -428,11 +424,14 @@ public final class Generator {
         new Handler<IRubyObject>() {
             @Override
             RubyString generateNew(Session session, IRubyObject object) {
-                IRubyObject result =
-                    object.callMethod(session.getContext(), "to_json",
-                          new IRubyObject[] {session.getState()});
-                if (result instanceof RubyString) return (RubyString)result;
-                throw session.getRuntime().newTypeError("to_json must return a String");
+                if (object.respondsTo("to_json")) {
+                    IRubyObject result = object.callMethod(session.getContext(), "to_json",
+                              new IRubyObject[] {session.getState()});
+                    if (result instanceof RubyString) return (RubyString)result;
+                    throw session.getRuntime().newTypeError("to_json must return a String");
+                } else {
+                    return OBJECT_HANDLER.generateNew(session, object);
+                }
             }
 
             @Override
diff --git a/java/src/json/ext/GeneratorMethods.java b/java/src/json/ext/GeneratorMethods.java
index 637b579..bde7a18 100644
--- a/java/src/json/ext/GeneratorMethods.java
+++ b/java/src/json/ext/GeneratorMethods.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
diff --git a/java/src/json/ext/GeneratorService.java b/java/src/json/ext/GeneratorService.java
index ed33639..e665ad1 100644
--- a/java/src/json/ext/GeneratorService.java
+++ b/java/src/json/ext/GeneratorService.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java
index 3065307..44a9311 100644
--- a/java/src/json/ext/GeneratorState.java
+++ b/java/src/json/ext/GeneratorState.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
@@ -208,45 +207,11 @@ public class GeneratorState extends RubyObject {
     @JRubyMethod
     public IRubyObject generate(ThreadContext context, IRubyObject obj) {
         RubyString result = Generator.generateJson(context, obj, this);
-        if (!quirksMode && !objectOrArrayLiteral(result)) {
-            throw Utils.newException(context, Utils.M_GENERATOR_ERROR,
-                    "only generation of JSON objects or arrays allowed");
-        }
         RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
-        if (info.encodingsSupported()) {
-            result.force_encoding(context, info.utf8.get());
-        }
+        result.force_encoding(context, info.utf8.get());
         return result;
     }
 
-    /**
-     * Ensures the given string is in the form "[...]" or "{...}", being
-     * possibly surrounded by white space.
-     * The string's encoding must be ASCII-compatible.
-     * @param value
-     * @return
-     */
-    private static boolean objectOrArrayLiteral(RubyString value) {
-        ByteList bl = value.getByteList();
-        int len = bl.length();
-
-        for (int pos = 0; pos < len - 1; pos++) {
-            int b = bl.get(pos);
-            if (Character.isWhitespace(b)) continue;
-
-            // match the opening brace
-            switch (b) {
-            case '[':
-                return matchClosingBrace(bl, pos, len, ']');
-            case '{':
-                return matchClosingBrace(bl, pos, len, '}');
-            default:
-                return false;
-            }
-        }
-        return false;
-    }
-
     private static boolean matchClosingBrace(ByteList bl, int pos, int len,
                                              int brace) {
         for (int endPos = len - 1; endPos > pos; endPos--) {
@@ -399,17 +364,6 @@ public class GeneratorState extends RubyObject {
         return context.getRuntime().newBoolean(asciiOnly);
     }
 
-    @JRubyMethod(name="quirks_mode")
-    public RubyBoolean quirks_mode_get(ThreadContext context) {
-        return context.getRuntime().newBoolean(quirksMode);
-    }
-
-    @JRubyMethod(name="quirks_mode=")
-    public IRubyObject quirks_mode_set(IRubyObject quirks_mode) {
-        quirksMode = quirks_mode.isTrue();
-        return quirks_mode.getRuntime().newBoolean(quirksMode);
-    }
-
     @JRubyMethod(name="buffer_initial_length")
     public RubyInteger buffer_initial_length_get(ThreadContext context) {
         return context.getRuntime().newFixnum(bufferInitialLength);
@@ -422,11 +376,6 @@ public class GeneratorState extends RubyObject {
         return buffer_initial_length;
     }
 
-    @JRubyMethod(name="quirks_mode?")
-    public RubyBoolean quirks_mode_p(ThreadContext context) {
-        return context.getRuntime().newBoolean(quirksMode);
-    }
-
     public int getDepth() {
         return depth;
     }
@@ -445,7 +394,7 @@ public class GeneratorState extends RubyObject {
     private ByteList prepareByteList(ThreadContext context, IRubyObject value) {
         RubyString str = value.convertToString();
         RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
-        if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) {
+        if (str.encoding(context) != info.utf8.get()) {
             str = (RubyString)str.encode(context, info.utf8.get());
         }
         return str.getByteList().dup();
@@ -481,7 +430,6 @@ public class GeneratorState extends RubyObject {
         maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
         allowNaN   = opts.getBool("allow_nan",  DEFAULT_ALLOW_NAN);
         asciiOnly  = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY);
-        quirksMode = opts.getBool("quirks_mode", DEFAULT_QUIRKS_MODE);
         bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH);
 
         depth = opts.getInt("depth", 0);
@@ -508,7 +456,6 @@ public class GeneratorState extends RubyObject {
         result.op_aset(context, runtime.newSymbol("array_nl"), array_nl_get(context));
         result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context));
         result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context));
-        result.op_aset(context, runtime.newSymbol("quirks_mode"), quirks_mode_p(context));
         result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context));
         result.op_aset(context, runtime.newSymbol("depth"), depth_get(context));
         result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context));
diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java
index 8212503..70426d4 100644
--- a/java/src/json/ext/OptionsReader.java
+++ b/java/src/json/ext/OptionsReader.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
@@ -85,7 +84,7 @@ final class OptionsReader {
 
         RubyString str = value.convertToString();
         RuntimeInfo info = getRuntimeInfo();
-        if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) {
+        if (str.encoding(context) != info.utf8.get()) {
             str = (RubyString)str.encode(context, info.utf8.get());
         }
         return str;
diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java
index 37423f5..2ba9f8f 100644
--- a/java/src/json/ext/Parser.java
+++ b/java/src/json/ext/Parser.java
@@ -3,8 +3,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
@@ -53,10 +52,10 @@ public class Parser extends RubyObject {
     private int maxNesting;
     private boolean allowNaN;
     private boolean symbolizeNames;
-    private boolean quirksMode;
     private RubyClass objectClass;
     private RubyClass arrayClass;
-    private RubyHash match_string;
+    private RubyClass decimalClass;
+    private RubyHash matchString;
 
     private static final int DEFAULT_MAX_NESTING = 100;
 
@@ -124,10 +123,6 @@ public class Parser extends RubyObject {
      * <dd>If set to <code>true</code>, returns symbols for the names (keys) in
      * a JSON object. Otherwise strings are returned, which is also the default.
      *
-     * <dt><code>:quirks_mode?</code>
-     * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false
-     * otherwise.
-     * 
      * <dt><code>:create_additions</code>
      * <dd>If set to <code>false</code>, the Parser doesn't create additions
      * even if a matching class and <code>create_id</code> was found. This option
@@ -139,9 +134,10 @@ public class Parser extends RubyObject {
      * <dt><code>:array_class</code>
      * <dd>Defaults to Array.
      *
-     * <dt><code>:quirks_mode</code>
-     * <dd>Enables quirks_mode for parser, that is for example parsing single
-     * JSON values instead of documents is possible.
+     * <dt><code>:decimal_class</code>
+     * <dd>Specifies which class to use instead of the default (Float) when
+     * parsing decimal numbers. This class must accept a single string argument
+     * in its constructor.
      * </dl>
      */
     @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -164,15 +160,21 @@ public class Parser extends RubyObject {
         this.maxNesting      = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
         this.allowNaN        = opts.getBool("allow_nan", false);
         this.symbolizeNames  = opts.getBool("symbolize_names", false);
-        this.quirksMode      = opts.getBool("quirks_mode", false);
         this.createId        = opts.getString("create_id", getCreateId(context));
         this.createAdditions = opts.getBool("create_additions", false);
         this.objectClass     = opts.getClass("object_class", runtime.getHash());
         this.arrayClass      = opts.getClass("array_class", runtime.getArray());
-        this.match_string    = opts.getHash("match_string");
-
+        this.decimalClass    = opts.getClass("decimal_class", null);
+        this.matchString    = opts.getHash("match_string");
+
+        if(symbolizeNames && createAdditions) {
+          throw runtime.newArgumentError(
+            "options :symbolize_names and :create_additions cannot be " +
+            " used in conjunction"
+          );
+        }
         this.vSource = args[0].convertToString();
-        if (!quirksMode) this.vSource = convertEncoding(context, vSource);
+        this.vSource = convertEncoding(context, vSource);
 
         return this;
     }
@@ -183,33 +185,16 @@ public class Parser extends RubyObject {
      * Returns the source string if no conversion is needed.
      */
     private RubyString convertEncoding(ThreadContext context, RubyString source) {
-        ByteList bl = source.getByteList();
-        int len = bl.length();
-        if (len < 2) {
-            throw Utils.newException(context, Utils.M_PARSER_ERROR,
-                "A JSON text must at least contain two octets!");
-        }
-
-        if (info.encodingsSupported()) {
-            RubyEncoding encoding = (RubyEncoding)source.encoding(context);
-            if (encoding != info.ascii8bit.get()) {
-                return (RubyString)source.encode(context, info.utf8.get());
-            }
-
-            String sniffedEncoding = sniffByteList(bl);
-            if (sniffedEncoding == null) return source; // assume UTF-8
-            return reinterpretEncoding(context, source, sniffedEncoding);
-        }
-
-        String sniffedEncoding = sniffByteList(bl);
-        if (sniffedEncoding == null) return source; // assume UTF-8
-        Ruby runtime = context.getRuntime();
-        return (RubyString)info.jsonModule.get().
-            callMethod(context, "iconv",
-                new IRubyObject[] {
-                    runtime.newString("utf-8"),
-                    runtime.newString(sniffedEncoding),
-                    source});
+      RubyEncoding encoding = (RubyEncoding)source.encoding(context);
+      if (encoding == info.ascii8bit.get()) {
+          if (source.isFrozen()) {
+            source = (RubyString) source.dup();
+          }
+          source.force_encoding(context, info.utf8.get());
+      } else {
+        source = (RubyString) source.encode(context, info.utf8.get());
+      }
+      return source;
     }
 
     /**
@@ -262,17 +247,6 @@ public class Parser extends RubyObject {
         return checkAndGetSource().dup();
     }
 
-    /**
-     * <code>Parser#quirks_mode?()</code>
-     * 
-     * <p>If set to <code>true</code>, if the parse is in quirks_mode, false
-     * otherwise.
-     */
-    @JRubyMethod(name = "quirks_mode?")
-    public IRubyObject quirks_mode_p(ThreadContext context) {
-        return context.getRuntime().newBoolean(quirksMode);
-    }
-
     public RubyString checkAndGetSource() {
       if (vSource != null) {
         return vSource;
@@ -339,11 +313,11 @@ public class Parser extends RubyObject {
         }
 
         
-// line 365 "Parser.rl"
+// line 339 "Parser.rl"
 
 
         
-// line 347 "Parser.java"
+// line 321 "Parser.java"
 private static byte[] init__JSON_value_actions_0()
 {
 	return new byte [] {
@@ -457,7 +431,7 @@ static final int JSON_value_error = 0;
 static final int JSON_value_en_main = 1;
 
 
-// line 471 "Parser.rl"
+// line 445 "Parser.rl"
 
 
         void parseValue(ParserResult res, int p, int pe) {
@@ -465,14 +439,14 @@ static final int JSON_value_en_main = 1;
             IRubyObject result = null;
 
             
-// line 469 "Parser.java"
+// line 443 "Parser.java"
 	{
 	cs = JSON_value_start;
 	}
 
-// line 478 "Parser.rl"
+// line 452 "Parser.rl"
             
-// line 476 "Parser.java"
+// line 450 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -498,13 +472,13 @@ case 1:
 	while ( _nacts-- > 0 ) {
 		switch ( _JSON_value_actions[_acts++] ) {
 	case 9:
-// line 456 "Parser.rl"
+// line 430 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 508 "Parser.java"
+// line 482 "Parser.java"
 		}
 	}
 
@@ -567,25 +541,25 @@ case 1:
 			switch ( _JSON_value_actions[_acts++] )
 			{
 	case 0:
-// line 373 "Parser.rl"
+// line 347 "Parser.rl"
 	{
                 result = getRuntime().getNil();
             }
 	break;
 	case 1:
-// line 376 "Parser.rl"
+// line 350 "Parser.rl"
 	{
                 result = getRuntime().getFalse();
             }
 	break;
 	case 2:
-// line 379 "Parser.rl"
+// line 353 "Parser.rl"
 	{
                 result = getRuntime().getTrue();
             }
 	break;
 	case 3:
-// line 382 "Parser.rl"
+// line 356 "Parser.rl"
 	{
                 if (parser.allowNaN) {
                     result = getConstant(CONST_NAN);
@@ -595,7 +569,7 @@ case 1:
             }
 	break;
 	case 4:
-// line 389 "Parser.rl"
+// line 363 "Parser.rl"
 	{
                 if (parser.allowNaN) {
                     result = getConstant(CONST_INFINITY);
@@ -605,9 +579,9 @@ case 1:
             }
 	break;
 	case 5:
-// line 396 "Parser.rl"
+// line 370 "Parser.rl"
 	{
-                if (pe > p + 9 - (parser.quirksMode ? 1 : 0) &&
+                if (pe > p + 8 &&
                     absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) {
 
                     if (parser.allowNaN) {
@@ -634,7 +608,7 @@ case 1:
             }
 	break;
 	case 6:
-// line 422 "Parser.rl"
+// line 396 "Parser.rl"
 	{
                 parseString(res, p, pe);
                 if (res.result == null) {
@@ -647,7 +621,7 @@ case 1:
             }
 	break;
 	case 7:
-// line 432 "Parser.rl"
+// line 406 "Parser.rl"
 	{
                 currentNesting++;
                 parseArray(res, p, pe);
@@ -662,7 +636,7 @@ case 1:
             }
 	break;
 	case 8:
-// line 444 "Parser.rl"
+// line 418 "Parser.rl"
 	{
                 currentNesting++;
                 parseObject(res, p, pe);
@@ -676,7 +650,7 @@ case 1:
                 }
             }
 	break;
-// line 680 "Parser.java"
+// line 654 "Parser.java"
 			}
 		}
 	}
@@ -696,7 +670,7 @@ case 5:
 	break; }
 	}
 
-// line 479 "Parser.rl"
+// line 453 "Parser.rl"
 
             if (cs >= JSON_value_first_final && result != null) {
                 res.update(result, p);
@@ -706,7 +680,7 @@ case 5:
         }
 
         
-// line 710 "Parser.java"
+// line 684 "Parser.java"
 private static byte[] init__JSON_integer_actions_0()
 {
 	return new byte [] {
@@ -805,7 +779,7 @@ static final int JSON_integer_error = 0;
 static final int JSON_integer_en_main = 1;
 
 
-// line 498 "Parser.rl"
+// line 472 "Parser.rl"
 
 
         void parseInteger(ParserResult res, int p, int pe) {
@@ -823,15 +797,15 @@ static final int JSON_integer_en_main = 1;
             int cs = EVIL;
 
             
-// line 827 "Parser.java"
+// line 801 "Parser.java"
 	{
 	cs = JSON_integer_start;
 	}
 
-// line 515 "Parser.rl"
+// line 489 "Parser.rl"
             int memo = p;
             
-// line 835 "Parser.java"
+// line 809 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -912,13 +886,13 @@ case 1:
 			switch ( _JSON_integer_actions[_acts++] )
 			{
 	case 0:
-// line 492 "Parser.rl"
+// line 466 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 922 "Parser.java"
+// line 896 "Parser.java"
 			}
 		}
 	}
@@ -938,7 +912,7 @@ case 5:
 	break; }
 	}
 
-// line 517 "Parser.rl"
+// line 491 "Parser.rl"
 
             if (cs < JSON_integer_first_final) {
                 return -1;
@@ -946,13 +920,13 @@ case 5:
 
             return p;
         }
-        
+
         RubyInteger createInteger(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return bytesToInum(runtime, num);
         }
-        
+
         RubyInteger bytesToInum(Ruby runtime, ByteList num) {
             return runtime.is1_9() ?
                     ConvertBytes.byteListToInum19(runtime, num, 10, true) :
@@ -960,7 +934,7 @@ case 5:
         }
 
         
-// line 964 "Parser.java"
+// line 938 "Parser.java"
 private static byte[] init__JSON_float_actions_0()
 {
 	return new byte [] {
@@ -1062,7 +1036,7 @@ static final int JSON_float_error = 0;
 static final int JSON_float_en_main = 1;
 
 
-// line 552 "Parser.rl"
+// line 526 "Parser.rl"
 
 
         void parseFloat(ParserResult res, int p, int pe) {
@@ -1071,7 +1045,9 @@ static final int JSON_float_en_main = 1;
                 res.update(null, p);
                 return;
             }
-            RubyFloat number = createFloat(p, new_p);
+            IRubyObject number = parser.decimalClass == null ?
+                createFloat(p, new_p) : createCustomDecimal(p, new_p);
+
             res.update(number, new_p + 1);
             return;
         }
@@ -1080,15 +1056,15 @@ static final int JSON_float_en_main = 1;
             int cs = EVIL;
 
             
-// line 1084 "Parser.java"
+// line 1060 "Parser.java"
 	{
 	cs = JSON_float_start;
 	}
 
-// line 569 "Parser.rl"
+// line 545 "Parser.rl"
             int memo = p;
             
-// line 1092 "Parser.java"
+// line 1068 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1169,13 +1145,13 @@ case 1:
 			switch ( _JSON_float_actions[_acts++] )
 			{
 	case 0:
-// line 543 "Parser.rl"
+// line 517 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 1179 "Parser.java"
+// line 1155 "Parser.java"
 			}
 		}
 	}
@@ -1195,23 +1171,30 @@ case 5:
 	break; }
 	}
 
-// line 571 "Parser.rl"
+// line 547 "Parser.rl"
 
             if (cs < JSON_float_first_final) {
                 return -1;
             }
-            
+
             return p;
         }
-        
+
         RubyFloat createFloat(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
         }
 
+        IRubyObject createCustomDecimal(int p, int new_p) {
+            Ruby runtime = getRuntime();
+            ByteList num = absSubSequence(p, new_p);
+            IRubyObject numString = runtime.newString(num.toString());
+            return parser.decimalClass.callMethod(context, "new", numString);
+        }
+
         
-// line 1215 "Parser.java"
+// line 1198 "Parser.java"
 private static byte[] init__JSON_string_actions_0()
 {
 	return new byte [] {
@@ -1313,7 +1296,7 @@ static final int JSON_string_error = 0;
 static final int JSON_string_en_main = 1;
 
 
-// line 616 "Parser.rl"
+// line 599 "Parser.rl"
 
 
         void parseString(ParserResult res, int p, int pe) {
@@ -1321,15 +1304,15 @@ static final int JSON_string_en_main = 1;
             IRubyObject result = null;
 
             
-// line 1325 "Parser.java"
+// line 1308 "Parser.java"
 	{
 	cs = JSON_string_start;
 	}
 
-// line 623 "Parser.rl"
+// line 606 "Parser.rl"
             int memo = p;
             
-// line 1333 "Parser.java"
+// line 1316 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1410,7 +1393,7 @@ case 1:
 			switch ( _JSON_string_actions[_acts++] )
 			{
 	case 0:
-// line 591 "Parser.rl"
+// line 574 "Parser.rl"
 	{
                 int offset = byteList.begin();
                 ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
@@ -1425,13 +1408,13 @@ case 1:
             }
 	break;
 	case 1:
-// line 604 "Parser.rl"
+// line 587 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 1435 "Parser.java"
+// line 1418 "Parser.java"
 			}
 		}
 	}
@@ -1451,14 +1434,14 @@ case 5:
 	break; }
 	}
 
-// line 625 "Parser.rl"
+// line 608 "Parser.rl"
 
             if (parser.createAdditions) {
-                RubyHash match_string = parser.match_string;
-                if (match_string != null) {
+                RubyHash matchString = parser.matchString;
+                if (matchString != null) {
                     final IRubyObject[] memoArray = { result, null };
                     try {
-                      match_string.visitAll(new RubyHash.Visitor() {
+                      matchString.visitAll(new RubyHash.Visitor() {
                           @Override
                           public void visit(IRubyObject pattern, IRubyObject klass) {
                               if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
@@ -1478,8 +1461,8 @@ case 5:
                 }
             }
 
-            if (cs >= JSON_string_first_final && result != null) {                
-                if (info.encodingsSupported() && result instanceof RubyString) {
+            if (cs >= JSON_string_first_final && result != null) {
+                if (result instanceof RubyString) {
                   ((RubyString)result).force_encoding(context, info.utf8.get());
                 }
                 res.update(result, p + 1);
@@ -1489,7 +1472,7 @@ case 5:
         }
 
         
-// line 1493 "Parser.java"
+// line 1476 "Parser.java"
 private static byte[] init__JSON_array_actions_0()
 {
 	return new byte [] {
@@ -1602,7 +1585,7 @@ static final int JSON_array_error = 0;
 static final int JSON_array_en_main = 1;
 
 
-// line 698 "Parser.rl"
+// line 681 "Parser.rl"
 
 
         void parseArray(ParserResult res, int p, int pe) {
@@ -1622,14 +1605,14 @@ static final int JSON_array_en_main = 1;
             }
 
             
-// line 1626 "Parser.java"
+// line 1609 "Parser.java"
 	{
 	cs = JSON_array_start;
 	}
 
-// line 717 "Parser.rl"
+// line 700 "Parser.rl"
             
-// line 1633 "Parser.java"
+// line 1616 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1710,7 +1693,7 @@ case 1:
 			switch ( _JSON_array_actions[_acts++] )
 			{
 	case 0:
-// line 667 "Parser.rl"
+// line 650 "Parser.rl"
 	{
                 parseValue(res, p, pe);
                 if (res.result == null) {
@@ -1727,13 +1710,13 @@ case 1:
             }
 	break;
 	case 1:
-// line 682 "Parser.rl"
+// line 665 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 1737 "Parser.java"
+// line 1720 "Parser.java"
 			}
 		}
 	}
@@ -1753,7 +1736,7 @@ case 5:
 	break; }
 	}
 
-// line 718 "Parser.rl"
+// line 701 "Parser.rl"
 
             if (cs >= JSON_array_first_final) {
                 res.update(result, p + 1);
@@ -1763,7 +1746,7 @@ case 5:
         }
 
         
-// line 1767 "Parser.java"
+// line 1750 "Parser.java"
 private static byte[] init__JSON_object_actions_0()
 {
 	return new byte [] {
@@ -1886,7 +1869,7 @@ static final int JSON_object_error = 0;
 static final int JSON_object_en_main = 1;
 
 
-// line 777 "Parser.rl"
+// line 760 "Parser.rl"
 
 
         void parseObject(ParserResult res, int p, int pe) {
@@ -1911,14 +1894,14 @@ static final int JSON_object_en_main = 1;
             }
 
             
-// line 1915 "Parser.java"
+// line 1898 "Parser.java"
 	{
 	cs = JSON_object_start;
 	}
 
-// line 801 "Parser.rl"
+// line 784 "Parser.rl"
             
-// line 1922 "Parser.java"
+// line 1905 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -1999,7 +1982,7 @@ case 1:
 			switch ( _JSON_object_actions[_acts++] )
 			{
 	case 0:
-// line 732 "Parser.rl"
+// line 715 "Parser.rl"
 	{
                 parseValue(res, p, pe);
                 if (res.result == null) {
@@ -2016,7 +1999,7 @@ case 1:
             }
 	break;
 	case 1:
-// line 747 "Parser.rl"
+// line 730 "Parser.rl"
 	{
                 parseString(res, p, pe);
                 if (res.result == null) {
@@ -2036,13 +2019,13 @@ case 1:
             }
 	break;
 	case 2:
-// line 765 "Parser.rl"
+// line 748 "Parser.rl"
 	{
                 p--;
                 { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 2046 "Parser.java"
+// line 2029 "Parser.java"
 			}
 		}
 	}
@@ -2062,7 +2045,7 @@ case 5:
 	break; }
 	}
 
-// line 802 "Parser.rl"
+// line 785 "Parser.rl"
 
             if (cs < JSON_object_first_final) {
                 res.update(null, p + 1);
@@ -2095,11 +2078,11 @@ case 5:
         }
 
         
-// line 2099 "Parser.java"
+// line 2082 "Parser.java"
 private static byte[] init__JSON_actions_0()
 {
 	return new byte [] {
-	    0,    1,    0,    1,    1
+	    0,    1,    0
 	};
 }
 
@@ -2109,7 +2092,7 @@ private static final byte _JSON_actions[] = init__JSON_actions_0();
 private static byte[] init__JSON_key_offsets_0()
 {
 	return new byte [] {
-	    0,    0,    7,    9,   10,   12,   13,   15,   16,   18,   19
+	    0,    0,   16,   18,   19,   21,   22,   24,   25,   27,   28
 	};
 }
 
@@ -2119,9 +2102,9 @@ private static final byte _JSON_key_offsets[] = init__JSON_key_offsets_0();
 private static char[] init__JSON_trans_keys_0()
 {
 	return new char [] {
-	   13,   32,   47,   91,  123,    9,   10,   42,   47,   42,   42,   47,
-	   10,   42,   47,   42,   42,   47,   10,   13,   32,   47,    9,   10,
-	    0
+	   13,   32,   34,   45,   47,   73,   78,   91,  102,  110,  116,  123,
+	    9,   10,   48,   57,   42,   47,   42,   42,   47,   10,   42,   47,
+	   42,   42,   47,   10,   13,   32,   47,    9,   10,    0
 	};
 }
 
@@ -2131,7 +2114,7 @@ private static final char _JSON_trans_keys[] = init__JSON_trans_keys_0();
 private static byte[] init__JSON_single_lengths_0()
 {
 	return new byte [] {
-	    0,    5,    2,    1,    2,    1,    2,    1,    2,    1,    3
+	    0,   12,    2,    1,    2,    1,    2,    1,    2,    1,    3
 	};
 }
 
@@ -2141,7 +2124,7 @@ private static final byte _JSON_single_lengths[] = init__JSON_single_lengths_0()
 private static byte[] init__JSON_range_lengths_0()
 {
 	return new byte [] {
-	    0,    1,    0,    0,    0,    0,    0,    0,    0,    0,    1
+	    0,    2,    0,    0,    0,    0,    0,    0,    0,    0,    1
 	};
 }
 
@@ -2151,7 +2134,7 @@ private static final byte _JSON_range_lengths[] = init__JSON_range_lengths_0();
 private static byte[] init__JSON_index_offsets_0()
 {
 	return new byte [] {
-	    0,    0,    7,   10,   12,   15,   17,   20,   22,   25,   27
+	    0,    0,   15,   18,   20,   23,   25,   28,   30,   33,   35
 	};
 }
 
@@ -2161,9 +2144,10 @@ private static final byte _JSON_index_offsets[] = init__JSON_index_offsets_0();
 private static byte[] init__JSON_indicies_0()
 {
 	return new byte [] {
-	    0,    0,    2,    3,    4,    0,    1,    5,    6,    1,    7,    5,
-	    7,    0,    5,    0,    6,    8,    9,    1,   10,    8,   10,   11,
-	    8,   11,    9,   11,   11,   12,   11,    1,    0
+	    0,    0,    2,    2,    3,    2,    2,    2,    2,    2,    2,    2,
+	    0,    2,    1,    4,    5,    1,    6,    4,    6,    7,    4,    7,
+	    5,    8,    9,    1,   10,    8,   10,    0,    8,    0,    9,    7,
+	    7,   11,    7,    1,    0
 	};
 }
 
@@ -2173,8 +2157,7 @@ private static final byte _JSON_indicies[] = init__JSON_indicies_0();
 private static byte[] init__JSON_trans_targs_0()
 {
 	return new byte [] {
-	    1,    0,    2,   10,   10,    3,    5,    4,    7,    9,    8,   10,
-	    6
+	    1,    0,   10,    6,    3,    5,    4,   10,    7,    9,    8,    2
 	};
 }
 
@@ -2184,8 +2167,7 @@ private static final byte _JSON_trans_targs[] = init__JSON_trans_targs_0();
 private static byte[] init__JSON_trans_actions_0()
 {
 	return new byte [] {
-	    0,    0,    0,    3,    1,    0,    0,    0,    0,    0,    0,    0,
-	    0
+	    0,    0,    1,    0,    0,    0,    0,    0,    0,    0,    0,    0
 	};
 }
 
@@ -2199,26 +2181,26 @@ static final int JSON_error = 0;
 static final int JSON_en_main = 1;
 
 
-// line 867 "Parser.rl"
+// line 836 "Parser.rl"
 
 
-        public IRubyObject parseStrict() {
+        public IRubyObject parseImplemetation() {
             int cs = EVIL;
             int p, pe;
             IRubyObject result = null;
             ParserResult res = new ParserResult();
 
             
-// line 2213 "Parser.java"
+// line 2195 "Parser.java"
 	{
 	cs = JSON_start;
 	}
 
-// line 876 "Parser.rl"
+// line 845 "Parser.rl"
             p = byteList.begin();
             pe = p + byteList.length();
             
-// line 2222 "Parser.java"
+// line 2204 "Parser.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -2299,267 +2281,7 @@ case 1:
 			switch ( _JSON_actions[_acts++] )
 			{
 	case 0:
-// line 839 "Parser.rl"
-	{
-                currentNesting = 1;
-                parseObject(res, p, pe);
-                if (res.result == null) {
-                    p--;
-                    { p += 1; _goto_targ = 5; if (true)  continue _goto;}
-                } else {
-                    result = res.result;
-                    {p = (( res.p))-1;}
-                }
-            }
-	break;
-	case 1:
-// line 851 "Parser.rl"
-	{
-                currentNesting = 1;
-                parseArray(res, p, pe);
-                if (res.result == null) {
-                    p--;
-                    { p += 1; _goto_targ = 5; if (true)  continue _goto;}
-                } else {
-                    result = res.result;
-                    {p = (( res.p))-1;}
-                }
-            }
-	break;
-// line 2330 "Parser.java"
-			}
-		}
-	}
-
-case 2:
-	if ( cs == 0 ) {
-		_goto_targ = 5;
-		continue _goto;
-	}
-	if ( ++p != pe ) {
-		_goto_targ = 1;
-		continue _goto;
-	}
-case 4:
-case 5:
-	}
-	break; }
-	}
-
-// line 879 "Parser.rl"
-
-            if (cs >= JSON_first_final && p == pe) {
-                return result;
-            } else {
-                throw unexpectedToken(p, pe);
-            }
-        }
-
-        
-// line 2360 "Parser.java"
-private static byte[] init__JSON_quirks_mode_actions_0()
-{
-	return new byte [] {
-	    0,    1,    0
-	};
-}
-
-private static final byte _JSON_quirks_mode_actions[] = init__JSON_quirks_mode_actions_0();
-
-
-private static byte[] init__JSON_quirks_mode_key_offsets_0()
-{
-	return new byte [] {
-	    0,    0,   16,   18,   19,   21,   22,   24,   25,   27,   28
-	};
-}
-
-private static final byte _JSON_quirks_mode_key_offsets[] = init__JSON_quirks_mode_key_offsets_0();
-
-
-private static char[] init__JSON_quirks_mode_trans_keys_0()
-{
-	return new char [] {
-	   13,   32,   34,   45,   47,   73,   78,   91,  102,  110,  116,  123,
-	    9,   10,   48,   57,   42,   47,   42,   42,   47,   10,   42,   47,
-	   42,   42,   47,   10,   13,   32,   47,    9,   10,    0
-	};
-}
-
-private static final char _JSON_quirks_mode_trans_keys[] = init__JSON_quirks_mode_trans_keys_0();
-
-
-private static byte[] init__JSON_quirks_mode_single_lengths_0()
-{
-	return new byte [] {
-	    0,   12,    2,    1,    2,    1,    2,    1,    2,    1,    3
-	};
-}
-
-private static final byte _JSON_quirks_mode_single_lengths[] = init__JSON_quirks_mode_single_lengths_0();
-
-
-private static byte[] init__JSON_quirks_mode_range_lengths_0()
-{
-	return new byte [] {
-	    0,    2,    0,    0,    0,    0,    0,    0,    0,    0,    1
-	};
-}
-
-private static final byte _JSON_quirks_mode_range_lengths[] = init__JSON_quirks_mode_range_lengths_0();
-
-
-private static byte[] init__JSON_quirks_mode_index_offsets_0()
-{
-	return new byte [] {
-	    0,    0,   15,   18,   20,   23,   25,   28,   30,   33,   35
-	};
-}
-
-private static final byte _JSON_quirks_mode_index_offsets[] = init__JSON_quirks_mode_index_offsets_0();
-
-
-private static byte[] init__JSON_quirks_mode_indicies_0()
-{
-	return new byte [] {
-	    0,    0,    2,    2,    3,    2,    2,    2,    2,    2,    2,    2,
-	    0,    2,    1,    4,    5,    1,    6,    4,    6,    7,    4,    7,
-	    5,    8,    9,    1,   10,    8,   10,    0,    8,    0,    9,    7,
-	    7,   11,    7,    1,    0
-	};
-}
-
-private static final byte _JSON_quirks_mode_indicies[] = init__JSON_quirks_mode_indicies_0();
-
-
-private static byte[] init__JSON_quirks_mode_trans_targs_0()
-{
-	return new byte [] {
-	    1,    0,   10,    6,    3,    5,    4,   10,    7,    9,    8,    2
-	};
-}
-
-private static final byte _JSON_quirks_mode_trans_targs[] = init__JSON_quirks_mode_trans_targs_0();
-
-
-private static byte[] init__JSON_quirks_mode_trans_actions_0()
-{
-	return new byte [] {
-	    0,    0,    1,    0,    0,    0,    0,    0,    0,    0,    0,    0
-	};
-}
-
-private static final byte _JSON_quirks_mode_trans_actions[] = init__JSON_quirks_mode_trans_actions_0();
-
-
-static final int JSON_quirks_mode_start = 1;
-static final int JSON_quirks_mode_first_final = 10;
-static final int JSON_quirks_mode_error = 0;
-
-static final int JSON_quirks_mode_en_main = 1;
-
-
-// line 907 "Parser.rl"
-
-
-        public IRubyObject parseQuirksMode() {
-            int cs = EVIL;
-            int p, pe;
-            IRubyObject result = null;
-            ParserResult res = new ParserResult();
-
-            
-// line 2473 "Parser.java"
-	{
-	cs = JSON_quirks_mode_start;
-	}
-
-// line 916 "Parser.rl"
-            p = byteList.begin();
-            pe = p + byteList.length();
-            
-// line 2482 "Parser.java"
-	{
-	int _klen;
-	int _trans = 0;
-	int _acts;
-	int _nacts;
-	int _keys;
-	int _goto_targ = 0;
-
-	_goto: while (true) {
-	switch ( _goto_targ ) {
-	case 0:
-	if ( p == pe ) {
-		_goto_targ = 4;
-		continue _goto;
-	}
-	if ( cs == 0 ) {
-		_goto_targ = 5;
-		continue _goto;
-	}
-case 1:
-	_match: do {
-	_keys = _JSON_quirks_mode_key_offsets[cs];
-	_trans = _JSON_quirks_mode_index_offsets[cs];
-	_klen = _JSON_quirks_mode_single_lengths[cs];
-	if ( _klen > 0 ) {
-		int _lower = _keys;
-		int _mid;
-		int _upper = _keys + _klen - 1;
-		while (true) {
-			if ( _upper < _lower )
-				break;
-
-			_mid = _lower + ((_upper-_lower) >> 1);
-			if ( data[p] < _JSON_quirks_mode_trans_keys[_mid] )
-				_upper = _mid - 1;
-			else if ( data[p] > _JSON_quirks_mode_trans_keys[_mid] )
-				_lower = _mid + 1;
-			else {
-				_trans += (_mid - _keys);
-				break _match;
-			}
-		}
-		_keys += _klen;
-		_trans += _klen;
-	}
-
-	_klen = _JSON_quirks_mode_range_lengths[cs];
-	if ( _klen > 0 ) {
-		int _lower = _keys;
-		int _mid;
-		int _upper = _keys + (_klen<<1) - 2;
-		while (true) {
-			if ( _upper < _lower )
-				break;
-
-			_mid = _lower + (((_upper-_lower) >> 1) & ~1);
-			if ( data[p] < _JSON_quirks_mode_trans_keys[_mid] )
-				_upper = _mid - 2;
-			else if ( data[p] > _JSON_quirks_mode_trans_keys[_mid+1] )
-				_lower = _mid + 2;
-			else {
-				_trans += ((_mid - _keys)>>1);
-				break _match;
-			}
-		}
-		_trans += _klen;
-	}
-	} while (false);
-
-	_trans = _JSON_quirks_mode_indicies[_trans];
-	cs = _JSON_quirks_mode_trans_targs[_trans];
-
-	if ( _JSON_quirks_mode_trans_actions[_trans] != 0 ) {
-		_acts = _JSON_quirks_mode_trans_actions[_trans];
-		_nacts = (int) _JSON_quirks_mode_actions[_acts++];
-		while ( _nacts-- > 0 )
-	{
-			switch ( _JSON_quirks_mode_actions[_acts++] )
-			{
-	case 0:
-// line 893 "Parser.rl"
+// line 822 "Parser.rl"
 	{
                 parseValue(res, p, pe);
                 if (res.result == null) {
@@ -2571,7 +2293,7 @@ case 1:
                 }
             }
 	break;
-// line 2575 "Parser.java"
+// line 2297 "Parser.java"
 			}
 		}
 	}
@@ -2591,9 +2313,9 @@ case 5:
 	break; }
 	}
 
-// line 919 "Parser.rl"
+// line 848 "Parser.rl"
 
-            if (cs >= JSON_quirks_mode_first_final && p == pe) {
+            if (cs >= JSON_first_final && p == pe) {
                 return result;
             } else {
                 throw unexpectedToken(p, pe);
@@ -2601,12 +2323,7 @@ case 5:
         }
 
         public IRubyObject parse() {
-          if (parser.quirksMode) {
-            return parseQuirksMode();
-          } else {
-            return parseStrict();
-          }
-
+            return parseImplemetation();
         }
 
         /**
diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl
index 6d65963..4d170e1 100644
--- a/java/src/json/ext/Parser.rl
+++ b/java/src/json/ext/Parser.rl
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
@@ -51,9 +50,9 @@ public class Parser extends RubyObject {
     private int maxNesting;
     private boolean allowNaN;
     private boolean symbolizeNames;
-    private boolean quirksMode;
     private RubyClass objectClass;
     private RubyClass arrayClass;
+    private RubyClass decimalClass;
     private RubyHash match_string;
 
     private static final int DEFAULT_MAX_NESTING = 100;
@@ -122,10 +121,6 @@ public class Parser extends RubyObject {
      * <dd>If set to <code>true</code>, returns symbols for the names (keys) in
      * a JSON object. Otherwise strings are returned, which is also the default.
      *
-     * <dt><code>:quirks_mode?</code>
-     * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false
-     * otherwise.
-     * 
      * <dt><code>:create_additions</code>
      * <dd>If set to <code>false</code>, the Parser doesn't create additions
      * even if a matching class and <code>create_id</code> was found. This option
@@ -137,9 +132,10 @@ public class Parser extends RubyObject {
      * <dt><code>:array_class</code>
      * <dd>Defaults to Array.
      *
-     * <dt><code>:quirks_mode</code>
-     * <dd>Enables quirks_mode for parser, that is for example parsing single
-     * JSON values instead of documents is possible.
+     * <dt><code>:decimal_class</code>
+     * <dd>Specifies which class to use instead of the default (Float) when
+     * parsing decimal numbers. This class must accept a single string argument
+     * in its constructor.
      * </dl>
      */
     @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -162,15 +158,21 @@ public class Parser extends RubyObject {
         this.maxNesting      = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
         this.allowNaN        = opts.getBool("allow_nan", false);
         this.symbolizeNames  = opts.getBool("symbolize_names", false);
-        this.quirksMode      = opts.getBool("quirks_mode", false);
         this.createId        = opts.getString("create_id", getCreateId(context));
         this.createAdditions = opts.getBool("create_additions", false);
         this.objectClass     = opts.getClass("object_class", runtime.getHash());
         this.arrayClass      = opts.getClass("array_class", runtime.getArray());
+        this.decimalClass    = opts.getClass("decimal_class", null);
         this.match_string    = opts.getHash("match_string");
 
+        if(symbolizeNames && createAdditions) {
+          throw runtime.newArgumentError(
+            "options :symbolize_names and :create_additions cannot be " +
+            " used in conjunction"
+          );
+        }
         this.vSource = args[0].convertToString();
-        if (!quirksMode) this.vSource = convertEncoding(context, vSource);
+        this.vSource = convertEncoding(context, vSource);
 
         return this;
     }
@@ -181,33 +183,16 @@ public class Parser extends RubyObject {
      * Returns the source string if no conversion is needed.
      */
     private RubyString convertEncoding(ThreadContext context, RubyString source) {
-        ByteList bl = source.getByteList();
-        int len = bl.length();
-        if (len < 2) {
-            throw Utils.newException(context, Utils.M_PARSER_ERROR,
-                "A JSON text must at least contain two octets!");
-        }
-
-        if (info.encodingsSupported()) {
-            RubyEncoding encoding = (RubyEncoding)source.encoding(context);
-            if (encoding != info.ascii8bit.get()) {
-                return (RubyString)source.encode(context, info.utf8.get());
-            }
-
-            String sniffedEncoding = sniffByteList(bl);
-            if (sniffedEncoding == null) return source; // assume UTF-8
-            return reinterpretEncoding(context, source, sniffedEncoding);
-        }
-
-        String sniffedEncoding = sniffByteList(bl);
-        if (sniffedEncoding == null) return source; // assume UTF-8
-        Ruby runtime = context.getRuntime();
-        return (RubyString)info.jsonModule.get().
-            callMethod(context, "iconv",
-                new IRubyObject[] {
-                    runtime.newString("utf-8"),
-                    runtime.newString(sniffedEncoding),
-                    source});
+      RubyEncoding encoding = (RubyEncoding)source.encoding(context);
+      if (encoding == info.ascii8bit.get()) {
+          if (source.isFrozen()) {
+            source = (RubyString) source.dup();
+          }
+          source.force_encoding(context, info.utf8.get());
+      } else {
+        source = (RubyString) source.encode(context, info.utf8.get());
+      }
+      return source;
     }
 
     /**
@@ -260,17 +245,6 @@ public class Parser extends RubyObject {
         return checkAndGetSource().dup();
     }
 
-    /**
-     * <code>Parser#quirks_mode?()</code>
-     * 
-     * <p>If set to <code>true</code>, if the parse is in quirks_mode, false
-     * otherwise.
-     */
-    @JRubyMethod(name = "quirks_mode?")
-    public IRubyObject quirks_mode_p(ThreadContext context) {
-        return context.getRuntime().newBoolean(quirksMode);
-    }
-
     public RubyString checkAndGetSource() {
       if (vSource != null) {
         return vSource;
@@ -394,7 +368,7 @@ public class Parser extends RubyObject {
                 }
             }
             action parse_number {
-                if (pe > fpc + 9 - (parser.quirksMode ? 1 : 0) &&
+                if (pe > fpc + 8 &&
                     absSubSequence(fpc, fpc + 9).equals(JSON_MINUS_INFINITY)) {
 
                     if (parser.allowNaN) {
@@ -521,13 +495,13 @@ public class Parser extends RubyObject {
 
             return p;
         }
-        
+
         RubyInteger createInteger(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return bytesToInum(runtime, num);
         }
-        
+
         RubyInteger bytesToInum(Ruby runtime, ByteList num) {
             return runtime.is1_9() ?
                     ConvertBytes.byteListToInum19(runtime, num, 10, true) :
@@ -557,7 +531,9 @@ public class Parser extends RubyObject {
                 res.update(null, p);
                 return;
             }
-            RubyFloat number = createFloat(p, new_p);
+            IRubyObject number = parser.decimalClass == null ?
+                createFloat(p, new_p) : createCustomDecimal(p, new_p);
+
             res.update(number, new_p + 1);
             return;
         }
@@ -572,16 +548,23 @@ public class Parser extends RubyObject {
             if (cs < JSON_float_first_final) {
                 return -1;
             }
-            
+
             return p;
         }
-        
+
         RubyFloat createFloat(int p, int new_p) {
             Ruby runtime = getRuntime();
             ByteList num = absSubSequence(p, new_p);
             return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
         }
 
+        IRubyObject createCustomDecimal(int p, int new_p) {
+            Ruby runtime = getRuntime();
+            ByteList num = absSubSequence(p, new_p);
+            IRubyObject numString = runtime.newString(num.toString());
+            return parser.decimalClass.callMethod(context, "new", numString);
+        }
+
         %%{
             machine JSON_string;
             include JSON_common;
@@ -624,11 +607,11 @@ public class Parser extends RubyObject {
             %% write exec;
 
             if (parser.createAdditions) {
-                RubyHash match_string = parser.match_string;
-                if (match_string != null) {
+                RubyHash matchString = parser.matchString;
+                if (matchString != null) {
                     final IRubyObject[] memoArray = { result, null };
                     try {
-                      match_string.visitAll(new RubyHash.Visitor() {
+                      matchString.visitAll(new RubyHash.Visitor() {
                           @Override
                           public void visit(IRubyObject pattern, IRubyObject klass) {
                               if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
@@ -648,8 +631,8 @@ public class Parser extends RubyObject {
                 }
             }
 
-            if (cs >= JSON_string_first_final && result != null) {                
-                if (info.encodingsSupported() && result instanceof RubyString) {
+            if (cs >= JSON_string_first_final && result != null) {
+                if (result instanceof RubyString) {
                   ((RubyString)result).force_encoding(context, info.utf8.get());
                 }
                 res.update(result, p + 1);
@@ -766,7 +749,7 @@ public class Parser extends RubyObject {
                 fhold;
                 fbreak;
             }
-            
+
             pair      = ignore* begin_name >parse_name ignore* name_separator
               ignore* begin_value >parse_value;
             next_pair = ignore* value_separator pair;
@@ -836,60 +819,6 @@ public class Parser extends RubyObject {
 
             write data;
 
-            action parse_object {
-                currentNesting = 1;
-                parseObject(res, fpc, pe);
-                if (res.result == null) {
-                    fhold;
-                    fbreak;
-                } else {
-                    result = res.result;
-                    fexec res.p;
-                }
-            }
-
-            action parse_array {
-                currentNesting = 1;
-                parseArray(res, fpc, pe);
-                if (res.result == null) {
-                    fhold;
-                    fbreak;
-                } else {
-                    result = res.result;
-                    fexec res.p;
-                }
-            }
-
-            main := ignore*
-                    ( begin_object >parse_object
-                    | begin_array >parse_array )
-                    ignore*;
-        }%%
-
-        public IRubyObject parseStrict() {
-            int cs = EVIL;
-            int p, pe;
-            IRubyObject result = null;
-            ParserResult res = new ParserResult();
-
-            %% write init;
-            p = byteList.begin();
-            pe = p + byteList.length();
-            %% write exec;
-
-            if (cs >= JSON_first_final && p == pe) {
-                return result;
-            } else {
-                throw unexpectedToken(p, pe);
-            }
-        }
-
-        %%{
-            machine JSON_quirks_mode;
-            include JSON_common;
-
-            write data;
-
             action parse_value {
                 parseValue(res, fpc, pe);
                 if (res.result == null) {
@@ -906,7 +835,7 @@ public class Parser extends RubyObject {
                     ignore*;
         }%%
 
-        public IRubyObject parseQuirksMode() {
+        public IRubyObject parseImplemetation() {
             int cs = EVIL;
             int p, pe;
             IRubyObject result = null;
@@ -917,7 +846,7 @@ public class Parser extends RubyObject {
             pe = p + byteList.length();
             %% write exec;
 
-            if (cs >= JSON_quirks_mode_first_final && p == pe) {
+            if (cs >= JSON_first_final && p == pe) {
                 return result;
             } else {
                 throw unexpectedToken(p, pe);
@@ -925,12 +854,7 @@ public class Parser extends RubyObject {
         }
 
         public IRubyObject parse() {
-          if (parser.quirksMode) {
-            return parseQuirksMode();
-          } else {
-            return parseStrict();
-          }
-
+            return parseImplemetation();
         }
 
         /**
diff --git a/java/src/json/ext/ParserService.java b/java/src/json/ext/ParserService.java
index dde8834..b6015f9 100644
--- a/java/src/json/ext/ParserService.java
+++ b/java/src/json/ext/ParserService.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
diff --git a/java/src/json/ext/RuntimeInfo.java b/java/src/json/ext/RuntimeInfo.java
index 5de5740..2323bd9 100644
--- a/java/src/json/ext/RuntimeInfo.java
+++ b/java/src/json/ext/RuntimeInfo.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
@@ -91,10 +90,6 @@ final class RuntimeInfo {
         }
     }
 
-    public boolean encodingsSupported() {
-        return utf8 != null && utf8.get() != null;
-    }
-
     public RubyEncoding getEncoding(ThreadContext context, String name) {
         synchronized (encodings) {
             WeakReference<RubyEncoding> encoding = encodings.get(name);
diff --git a/java/src/json/ext/StringDecoder.java b/java/src/json/ext/StringDecoder.java
index 6023113..76cf183 100644
--- a/java/src/json/ext/StringDecoder.java
+++ b/java/src/json/ext/StringDecoder.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java
index 57bd19b..9d40dd3 100644
--- a/java/src/json/ext/StringEncoder.java
+++ b/java/src/json/ext/StringEncoder.java
@@ -1,3 +1,8 @@
+/*
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
+ *
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
+ */
 package json.ext;
 
 import org.jruby.exceptions.RaiseException;
diff --git a/java/src/json/ext/Utils.java b/java/src/json/ext/Utils.java
index 44d6a55..ed6f832 100644
--- a/java/src/json/ext/Utils.java
+++ b/java/src/json/ext/Utils.java
@@ -1,8 +1,7 @@
 /*
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
  *
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
- * for details.
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
  */
 package json.ext;
 
diff --git a/json-java.gemspec b/json-java.gemspec
index 14864f8..1524b1f 100644
--- a/json-java.gemspec
+++ b/json-java.gemspec
@@ -14,6 +14,21 @@ spec = Gem::Specification.new do |s|
   s.licenses = ["Ruby"]
 
   s.files = Dir["{docs,lib,tests}/**/*"]
+
+  if s.respond_to? :specification_version then
+    s.specification_version = 4
+
+    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+      s.add_development_dependency(%q<rake>, [">= 0"])
+      s.add_development_dependency(%q<test-unit>, ["~> 2.0"])
+    else
+      s.add_dependency(%q<rake>, [">= 0"])
+      s.add_dependency(%q<test-unit>, ["~> 2.0"])
+    end
+  else
+    s.add_dependency(%q<rake>, [">= 0"])
+    s.add_dependency(%q<test-unit>, ["~> 2.0"])
+  end
 end
 
 if $0 == __FILE__
diff --git a/json.gemspec b/json.gemspec
index 0b40989..b8f3009 100644
Binary files a/json.gemspec and b/json.gemspec differ
diff --git a/json_pure.gemspec b/json_pure.gemspec
index 3631d3c..4964a42 100644
--- a/json_pure.gemspec
+++ b/json_pure.gemspec
@@ -1,40 +1,38 @@
 # -*- encoding: utf-8 -*-
-# stub: json_pure 1.8.3 ruby lib
+# stub: json_pure 2.1.0 ruby lib
 
 Gem::Specification.new do |s|
-  s.name = "json_pure"
-  s.version = "1.8.3"
+  s.name = "json_pure".freeze
+  s.version = "2.1.0"
 
-  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
-  s.require_paths = ["lib"]
-  s.authors = ["Florian Frank"]
-  s.date = "2015-06-01"
-  s.description = "This is a JSON implementation in pure Ruby."
-  s.email = "flori at ping.de"
-  s.extra_rdoc_files = ["README.rdoc"]
-  s.files = ["./tests/test_json.rb", "./tests/test_json_addition.rb", "./tests/test_json_encoding.rb", "./tests/test_json_fixtures.rb", "./tests/test_json_generate.rb", "./tests/test_json_generic_object.rb", "./tests/test_json_string_matching.rb", "./tests/test_json_unicode.rb", ".gitignore", ".travis.yml", "CHANGES", "COPYING", "COPYING-json-jruby", "GPL", "Gemfile", "README-json-jruby.markdown", "README.rdoc", "Rakefile", "TODO", "VERSION", "data/example.json", "data/index.html", "data [...]
-  s.homepage = "http://flori.github.com/json"
-  s.licenses = ["Ruby"]
-  s.rdoc_options = ["--title", "JSON implemention for ruby", "--main", "README.rdoc"]
-  s.rubygems_version = "2.4.6"
-  s.summary = "JSON Implementation for Ruby"
-  s.test_files = ["./tests/test_json.rb", "./tests/test_json_addition.rb", "./tests/test_json_encoding.rb", "./tests/test_json_fixtures.rb", "./tests/test_json_generate.rb", "./tests/test_json_generic_object.rb", "./tests/test_json_string_matching.rb", "./tests/test_json_unicode.rb"]
+  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+  s.require_paths = ["lib".freeze]
+  s.authors = ["Florian Frank".freeze]
+  s.date = "2017-04-18"
+  s.description = "This is a JSON implementation in pure Ruby.".freeze
+  s.email = "flori at ping.de".freeze
+  s.extra_rdoc_files = ["README.md".freeze]
+  s.files = ["./tests/test_helper.rb".freeze, ".gitignore".freeze, ".travis.yml".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "README-json-jruby.md".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "data/example.json".freeze, "data/index.html".freeze, "data/prototype.js".freeze, "diagrams/.keep".freeze, "ext/json/ext/fbuffer/fbuffer.h".freeze, "ext/json/ext/generator/depend".freeze, "ext/json/ext/generator/extconf.rb".freeze, "ext/json/ext/generator/generator.c".freeze, [...]
+  s.homepage = "http://flori.github.com/json".freeze
+  s.licenses = ["Ruby".freeze]
+  s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze]
+  s.required_ruby_version = Gem::Requirement.new(">= 1.9".freeze)
+  s.rubygems_version = "2.6.11".freeze
+  s.summary = "JSON Implementation for Ruby".freeze
+  s.test_files = ["./tests/test_helper.rb".freeze]
 
   if s.respond_to? :specification_version then
     s.specification_version = 4
 
     if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
-      s.add_development_dependency(%q<permutation>, [">= 0"])
-      s.add_development_dependency(%q<sdoc>, ["~> 0.3.16"]) if RUBY_VERSION > "1.8.6"
-      s.add_development_dependency(%q<rake>, ["~> 0.9.2"])
+      s.add_development_dependency(%q<rake>.freeze, [">= 0"])
+      s.add_development_dependency(%q<test-unit>.freeze, ["~> 2.0"])
     else
-      s.add_dependency(%q<permutation>, [">= 0"])
-      s.add_dependency(%q<sdoc>, ["~> 0.3.16"]) if RUBY_VERSION > "1.8.6"
-      s.add_dependency(%q<rake>, ["~> 0.9.2"])
+      s.add_dependency(%q<rake>.freeze, [">= 0"])
+      s.add_dependency(%q<test-unit>.freeze, ["~> 2.0"])
     end
   else
-    s.add_dependency(%q<permutation>, [">= 0"])
-    s.add_dependency(%q<sdoc>, ["~> 0.3.16"]) if RUBY_VERSION > "1.8.6"
-    s.add_dependency(%q<rake>, ["~> 0.9.2"])
+    s.add_dependency(%q<rake>.freeze, [">= 0"])
+    s.add_dependency(%q<test-unit>.freeze, ["~> 2.0"])
   end
 end
diff --git a/lib/json.rb b/lib/json.rb
index 24aa385..b5a6912 100644
--- a/lib/json.rb
+++ b/lib/json.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 require 'json/common'
 
 ##
diff --git a/lib/json/add/bigdecimal.rb b/lib/json/add/bigdecimal.rb
index 0ef69f1..539daee 100644
--- a/lib/json/add/bigdecimal.rb
+++ b/lib/json/add/bigdecimal.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
diff --git a/lib/json/add/complex.rb b/lib/json/add/complex.rb
index 2723f60..28ef734 100644
--- a/lib/json/add/complex.rb
+++ b/lib/json/add/complex.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
@@ -25,4 +26,4 @@ class Complex
   def to_json(*)
     as_json.to_json
   end
-end
\ No newline at end of file
+end
diff --git a/lib/json/add/core.rb b/lib/json/add/core.rb
index 77d9dc0..bfb017c 100644
--- a/lib/json/add/core.rb
+++ b/lib/json/add/core.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 # This file requires the implementations of ruby core's custom objects for
 # serialisation/deserialisation.
 
diff --git a/lib/json/add/date.rb b/lib/json/add/date.rb
index 4288237..2552356 100644
--- a/lib/json/add/date.rb
+++ b/lib/json/add/date.rb
@@ -1,9 +1,9 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 require 'date'
 
-# Date serialization/deserialization
 class Date
 
   # Deserializes JSON string by converting Julian year <tt>y</tt>, month
diff --git a/lib/json/add/date_time.rb b/lib/json/add/date_time.rb
index 5ea42ea..38b0e86 100644
--- a/lib/json/add/date_time.rb
+++ b/lib/json/add/date_time.rb
@@ -1,9 +1,9 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 require 'date'
 
-# DateTime serialization/deserialization
 class DateTime
 
   # Deserializes JSON string by converting year <tt>y</tt>, month <tt>m</tt>,
diff --git a/lib/json/add/exception.rb b/lib/json/add/exception.rb
index e6ad257..a107e5b 100644
--- a/lib/json/add/exception.rb
+++ b/lib/json/add/exception.rb
@@ -1,8 +1,8 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 
-# Exception serialization/deserialization
 class Exception
 
   # Deserializes JSON string by constructing new Exception object with message
diff --git a/lib/json/add/ostruct.rb b/lib/json/add/ostruct.rb
index da81e10..e064c85 100644
--- a/lib/json/add/ostruct.rb
+++ b/lib/json/add/ostruct.rb
@@ -1,13 +1,13 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 require 'ostruct'
 
-# OpenStruct serialization/deserialization
 class OpenStruct
 
   # Deserializes JSON string by constructing new Struct object with values
-  # <tt>v</tt> serialized by <tt>to_json</tt>.
+  # <tt>t</tt> serialized by <tt>to_json</tt>.
   def self.json_create(object)
     new(object['t'] || object[:t])
   end
diff --git a/lib/json/add/range.rb b/lib/json/add/range.rb
index e61e553..93529fb 100644
--- a/lib/json/add/range.rb
+++ b/lib/json/add/range.rb
@@ -1,8 +1,8 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 
-# Range serialization/deserialization
 class Range
 
   # Deserializes JSON string by constructing new Range object with arguments
diff --git a/lib/json/add/rational.rb b/lib/json/add/rational.rb
index ee39c20..356940b 100644
--- a/lib/json/add/rational.rb
+++ b/lib/json/add/rational.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
diff --git a/lib/json/add/regexp.rb b/lib/json/add/regexp.rb
index 2fcbb6f..a93866b 100644
--- a/lib/json/add/regexp.rb
+++ b/lib/json/add/regexp.rb
@@ -1,8 +1,8 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 
-# Regexp serialization/deserialization
 class Regexp
 
   # Deserializes JSON string by constructing new Regexp object with source
diff --git a/lib/json/add/struct.rb b/lib/json/add/struct.rb
index 6847cde..e8395ed 100644
--- a/lib/json/add/struct.rb
+++ b/lib/json/add/struct.rb
@@ -1,8 +1,8 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 
-# Struct serialization/deserialization
 class Struct
 
   # Deserializes JSON string by constructing new Struct object with values
diff --git a/lib/json/add/symbol.rb b/lib/json/add/symbol.rb
index 03dc9a5..74b13a4 100644
--- a/lib/json/add/symbol.rb
+++ b/lib/json/add/symbol.rb
@@ -1,8 +1,8 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 
-# Symbol serialization/deserialization
 class Symbol
   # Returns a hash, that will be turned into a JSON object and represent this
   # object.
diff --git a/lib/json/add/time.rb b/lib/json/add/time.rb
index d983467..b73acc4 100644
--- a/lib/json/add/time.rb
+++ b/lib/json/add/time.rb
@@ -1,8 +1,8 @@
+#frozen_string_literal: false
 unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
   require 'json'
 end
 
-# Time serialization/deserialization
 class Time
 
   # Deserializes JSON string by converting time since epoch to Time
diff --git a/lib/json/common.rb b/lib/json/common.rb
index f44184e..7cc8529 100644
--- a/lib/json/common.rb
+++ b/lib/json/common.rb
@@ -1,14 +1,15 @@
+#frozen_string_literal: false
 require 'json/version'
 require 'json/generic_object'
 
 module JSON
   class << self
-    # 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.
+    # 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.
+    # 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
         JSON.parse(object.to_str, opts)
@@ -24,7 +25,7 @@ module JSON
     # Set the JSON parser class _parser_ to be used by JSON.
     def parser=(parser) # :nodoc:
       @parser = parser
-      remove_const :Parser if JSON.const_defined_in?(self, :Parser)
+      remove_const :Parser if const_defined?(:Parser, false)
       const_set :Parser, parser
     end
 
@@ -35,8 +36,8 @@ module JSON
     def deep_const_get(path) # :nodoc:
       path.to_s.split(/::/).inject(Object) do |p, c|
         case
-        when c.empty?                     then p
-        when JSON.const_defined_in?(p, c) then p.const_get(c)
+        when c.empty?                  then p
+        when p.const_defined?(c, true) then p.const_get(c)
         else
           begin
             p.const_missing(c)
@@ -138,10 +139,10 @@ module JSON
   # _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
-  #   to 100.
+  #   structures. Disable depth checking with :max_nesting => false. 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
+  #   defiance of RFC 7159 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. Strings are
@@ -161,11 +162,11 @@ module JSON
   #
   # _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
-  #   if someone wants to fill up your stack.
+  #   structures. Enable depth checking with :max_nesting => anInteger. The
+  #   parse! 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
+  #   defiance of RFC 7159 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 matching class and create_id was found. This option
@@ -174,7 +175,7 @@ module JSON
     opts = {
       :max_nesting  => false,
       :allow_nan    => true
-    }.update(opts)
+    }.merge(opts)
     Parser.new(source, opts).parse
   end
 
@@ -295,13 +296,13 @@ module JSON
     # The global default options for the JSON.load method:
     #  :max_nesting: false
     #  :allow_nan:   true
-    #  :quirks_mode: true
+    #  :allow_blank:  true
     attr_accessor :load_default_options
   end
   self.load_default_options = {
     :max_nesting      => false,
     :allow_nan        => true,
-    :quirks_mode      => true,
+    :allow_blank       => true,
     :create_additions => true,
   }
 
@@ -328,7 +329,7 @@ module JSON
     elsif source.respond_to?(:read)
       source = source.read
     end
-    if opts[:quirks_mode] && (source.nil? || source.empty?)
+    if opts[:allow_blank] && (source.nil? || source.empty?)
       source = 'null'
     end
     result = parse(source, opts)
@@ -357,13 +358,12 @@ module JSON
     # The global default options for the JSON.dump method:
     #  :max_nesting: false
     #  :allow_nan:   true
-    #  :quirks_mode: true
+    #  :allow_blank: 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
@@ -402,37 +402,9 @@ module JSON
     raise ArgumentError, "exceed depth limit"
   end
 
-  # Swap consecutive bytes of _string_ in place.
-  def self.swap!(string) # :nodoc:
-    0.upto(string.size / 2) do |i|
-      break unless string[2 * i + 1]
-      string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
-    end
-    string
-  end
-
-  # Shortcut for iconv.
-  if ::String.method_defined?(:encode)
-    # Encodes string using Ruby's _String.encode_
-    def self.iconv(to, from, string)
-      string.encode(to, from)
-    end
-  else
-    require 'iconv'
-    # Encodes string using _iconv_ library
-    def self.iconv(to, from, string)
-      Iconv.conv(to, from, string)
-    end
-  end
-
-  if ::Object.method(:const_defined?).arity == 1
-    def self.const_defined_in?(modul, constant)
-      modul.const_defined?(constant)
-    end
-  else
-    def self.const_defined_in?(modul, constant)
-      modul.const_defined?(constant, false)
-    end
+  # Encodes string using Ruby's _String.encode_
+  def self.iconv(to, from, string)
+    string.encode(to, from)
   end
 end
 
diff --git a/lib/json/ext.rb b/lib/json/ext.rb
index c5f8131..7264a85 100644
--- a/lib/json/ext.rb
+++ b/lib/json/ext.rb
@@ -1,9 +1,3 @@
-if ENV['SIMPLECOV_COVERAGE'].to_i == 1
-  require 'simplecov'
-  SimpleCov.start do
-    add_filter "/tests/"
-  end
-end
 require 'json/common'
 
 module JSON
diff --git a/lib/json/generic_object.rb b/lib/json/generic_object.rb
index 8b8fd53..108309d 100644
--- a/lib/json/generic_object.rb
+++ b/lib/json/generic_object.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 require 'ostruct'
 
 module JSON
@@ -48,12 +49,12 @@ module JSON
     end
 
     def [](name)
-      table[name.to_sym]
-    end
+      __send__(name)
+    end unless method_defined?(:[])
 
     def []=(name, value)
-      __send__ "#{name}=", value
-    end
+      __send__("#{name}=", value)
+    end unless method_defined?(:[]=)
 
     def |(other)
       self.class[other.to_hash.merge(to_hash)]
diff --git a/lib/json/pure.rb b/lib/json/pure.rb
index b68668b..53178b3 100644
--- a/lib/json/pure.rb
+++ b/lib/json/pure.rb
@@ -1,17 +1,11 @@
-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'
 
 module JSON
   # This module holds all the modules/classes that implement JSON's
   # functionality in pure ruby.
   module Pure
+    require 'json/pure/parser'
+    require 'json/pure/generator'
     $DEBUG and warn "Using Pure library for JSON."
     JSON.parser = Parser
     JSON.generator = Generator
diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb
index 9056a5d..ccb6fe4 100644
--- a/lib/json/pure/generator.rb
+++ b/lib/json/pure/generator.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 module JSON
   MAP = {
     "\x0" => '\u0000',
@@ -38,85 +39,45 @@ module JSON
 
   # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
   # UTF16 big endian characters as \u????, and return it.
-  if defined?(::Encoding)
-    def utf8_to_json(string) # :nodoc:
-      string = string.dup
-      string.force_encoding(::Encoding::ASCII_8BIT)
-      string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
-      string.force_encoding(::Encoding::UTF_8)
-      string
-    end
-
-    def utf8_to_json_ascii(string) # :nodoc:
-      string = string.dup
-      string.force_encoding(::Encoding::ASCII_8BIT)
-      string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
-      string.gsub!(/(
-                      (?:
-                        [\xc2-\xdf][\x80-\xbf]    |
-                        [\xe0-\xef][\x80-\xbf]{2} |
-                        [\xf0-\xf4][\x80-\xbf]{3}
-                      )+ |
-                      [\x80-\xc1\xf5-\xff]       # invalid
-                    )/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.wrap(e)
-    end
-
-    def valid_utf8?(string)
-      encoding = string.encoding
-      (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
-        string.valid_encoding?
-    end
-    module_function :valid_utf8?
-  else
-    def utf8_to_json(string) # :nodoc:
-      string.gsub(/["\\\x0-\x1f]/n) { MAP[$&] }
-    end
+  def utf8_to_json(string) # :nodoc:
+    string = string.dup
+    string.force_encoding(::Encoding::ASCII_8BIT)
+    string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
+    string.force_encoding(::Encoding::UTF_8)
+    string
+  end
 
-    def utf8_to_json_ascii(string) # :nodoc:
-      string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
-      string.gsub!(/(
-                      (?:
-                        [\xc2-\xdf][\x80-\xbf]    |
-                        [\xe0-\xef][\x80-\xbf]{2} |
-                        [\xf0-\xf4][\x80-\xbf]{3}
-                      )+ |
-                      [\x80-\xc1\xf5-\xff]       # invalid
-                    )/nx) { |c|
-        c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
-        s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
-        s.gsub!(/.{4}/n, '\\\\u\&')
-      }
-      string
-    rescue => e
-      raise GeneratorError.wrap(e)
-    end
+  def utf8_to_json_ascii(string) # :nodoc:
+    string = string.dup
+    string.force_encoding(::Encoding::ASCII_8BIT)
+    string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
+    string.gsub!(/(
+      (?:
+       [\xc2-\xdf][\x80-\xbf]    |
+       [\xe0-\xef][\x80-\xbf]{2} |
+       [\xf0-\xf4][\x80-\xbf]{3}
+      )+ |
+      [\x80-\xc1\xf5-\xff]       # invalid
+    )/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.wrap(e)
+  end
 
-    def valid_utf8?(string)
-      string =~
-         /\A( [\x09\x0a\x0d\x20-\x7e]         # ASCII
-         | [\xc2-\xdf][\x80-\xbf]             # non-overlong 2-byte
-         |  \xe0[\xa0-\xbf][\x80-\xbf]        # excluding overlongs
-         | [\xe1-\xec\xee\xef][\x80-\xbf]{2}  # straight 3-byte
-         |  \xed[\x80-\x9f][\x80-\xbf]        # excluding surrogates
-         |  \xf0[\x90-\xbf][\x80-\xbf]{2}     # planes 1-3
-         | [\xf1-\xf3][\x80-\xbf]{3}          # planes 4-15
-         |  \xf4[\x80-\x8f][\x80-\xbf]{2}     # plane 16
-        )*\z/nx
-    end
+  def valid_utf8?(string)
+    encoding = string.encoding
+    (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
+      string.valid_encoding?
   end
   module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8?
 
-
   module Pure
     module Generator
       # This class is used to create State instances, that are use to hold data
@@ -154,8 +115,6 @@ module JSON
         # * *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                 = ''
@@ -164,7 +123,6 @@ module JSON
           @array_nl              = ''
           @allow_nan             = false
           @ascii_only            = false
-          @quirks_mode           = false
           @buffer_initial_length = 1024
           configure opts
         end
@@ -190,10 +148,6 @@ module JSON
         # 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
 
@@ -233,11 +187,6 @@ module JSON
           @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)
@@ -259,7 +208,6 @@ module JSON
           @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)
           @buffer_initial_length ||= opts[:buffer_initial_length]
 
           if !opts.key?(:max_nesting) # defaults to 100
@@ -286,20 +234,14 @@ module JSON
 
         alias to_hash to_h
 
-        # Generates a valid JSON document from object +obj+ and returns the
-        # result. If no valid JSON document can be created this method raises a
+        # 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.
         def generate(obj)
           result = obj.to_json(self)
           JSON.valid_utf8?(result) or raise GeneratorError,
             "source sequence #{result.inspect} is illegal/malformed utf-8"
-          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
 
@@ -363,7 +305,11 @@ module JSON
               result << state.space_before
               result << ':'
               result << state.space
-              result << value.to_json(state)
+              if value.respond_to?(:to_json)
+                result << value.to_json(state)
+              else
+                result << %{"#{String(value)}"}
+              end
               first = false
             }
             depth = state.depth -= 1
@@ -398,7 +344,11 @@ module JSON
             each { |value|
               result << delim unless first
               result << state.indent * depth if indent
-              result << value.to_json(state)
+              if value.respond_to?(:to_json)
+                result << value.to_json(state)
+              else
+                result << %{"#{String(value)}"}
+              end
               first = false
             }
             depth = state.depth -= 1
@@ -437,34 +387,20 @@ module JSON
         end
 
         module String
-          if defined?(::Encoding)
-            # 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????.
-            def to_json(state = nil, *args)
-              state = State.from_state(state)
-              if encoding == ::Encoding::UTF_8
-                string = self
-              else
-                string = encode(::Encoding::UTF_8)
-              end
-              if state.ascii_only?
-                '"' << JSON.utf8_to_json_ascii(string) << '"'
-              else
-                '"' << JSON.utf8_to_json(string) << '"'
-              end
+          # 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????.
+          def to_json(state = nil, *args)
+            state = State.from_state(state)
+            if encoding == ::Encoding::UTF_8
+              string = self
+            else
+              string = encode(::Encoding::UTF_8)
             end
-          else
-            # 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????.
-            def to_json(state = nil, *args)
-              state = State.from_state(state)
-              if state.ascii_only?
-                '"' << JSON.utf8_to_json_ascii(self) << '"'
-              else
-                '"' << JSON.utf8_to_json(self) << '"'
-              end
+            if state.ascii_only?
+              '"' << JSON.utf8_to_json_ascii(string) << '"'
+            else
+              '"' << JSON.utf8_to_json(string) << '"'
             end
           end
 
diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb
index a41d1ee..3a6343b 100644
--- a/lib/json/pure/parser.rb
+++ b/lib/json/pure/parser.rb
@@ -1,3 +1,4 @@
+#frozen_string_literal: false
 require 'strscan'
 
 module JSON
@@ -48,7 +49,7 @@ module JSON
         )+
       )mx
 
-      UNPARSED = Object.new
+        UNPARSED = Object.new.freeze
 
       # Creates a new JSON::Pure::Parser instance for the string _source_.
       #
@@ -58,23 +59,23 @@ module JSON
       #   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
+      #   defiance of RFC 7159 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.
+      #   (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 true, the Parser creates
       #   additions when if a matching class and create_id was found. This
       #   option defaults to false.
       # * *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.
+      # * *decimal_class*: Specifies which class to use instead of the default
+      #    (Float) when parsing decimal numbers. This class must accept a single
+      #    string argument in its constructor.
       def initialize(source, opts = {})
         opts ||= {}
-        unless @quirks_mode = opts[:quirks_mode]
-          source = convert_encoding source
-        end
+        source = convert_encoding source
         super source
         if !opts.key?(:max_nesting) # defaults to 100
           @max_nesting = 100
@@ -90,56 +91,38 @@ module JSON
         else
           @create_additions = false
         end
+        @symbolize_names && @create_additions and raise ArgumentError,
+          'options :symbolize_names and :create_additions cannot be used '\
+          'in conjunction'
         @create_id = @create_additions ? JSON.create_id : nil
         @object_class = opts[:object_class] || Hash
         @array_class  = opts[:array_class] || Array
+        @decimal_class = opts[:decimal_class]
         @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.
+      # Parses the current JSON string _source_ and returns the
+      # complete data structure as a result.
       def parse
         reset
         obj = nil
-        if @quirks_mode
-          while !eos? && skip(IGNORE)
-          end
-          if eos?
-            raise ParserError, "source did not contain any JSON!"
-          else
-            obj = parse_value
-            obj == UNPARSED and raise ParserError, "source did not contain any JSON!"
-          end
+        while !eos? && skip(IGNORE) do end
+        if eos?
+          raise ParserError, "source is not valid JSON!"
         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!"
+          obj = parse_value
+          UNPARSED.equal?(obj) and raise ParserError,
+            "source is not valid JSON!"
         end
+        while !eos? && skip(IGNORE) do end
+        eos? or raise ParserError, "source is not valid JSON!"
         obj
       end
 
@@ -149,43 +132,12 @@ module JSON
         if source.respond_to?(:to_str)
           source = source.to_str
         else
-          raise TypeError, "#{source.inspect} is not like a string"
+          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
+        if source.encoding != ::Encoding::ASCII_8BIT
+          source = source.encode(::Encoding::UTF_8)
           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
@@ -245,7 +197,7 @@ module JSON
       def parse_value
         case
         when scan(FLOAT)
-          Float(self[1])
+          @decimal_class && @decimal_class.new(self[1]) || Float(self[1])
         when scan(INTEGER)
           Integer(self[1])
         when scan(TRUE)
@@ -254,7 +206,7 @@ module JSON
           false
         when scan(NULL)
           nil
-        when (string = parse_string) != UNPARSED
+        when !UNPARSED.equal?(string = parse_string)
           string
         when scan(ARRAY_OPEN)
           @current_nesting += 1
@@ -284,7 +236,7 @@ module JSON
         delim = false
         until eos?
           case
-          when (value = parse_value) != UNPARSED
+          when !UNPARSED.equal?(value = parse_value)
             delim = false
             result << value
             skip(IGNORE)
@@ -316,13 +268,13 @@ module JSON
         delim = false
         until eos?
           case
-          when (string = parse_string) != UNPARSED
+          when !UNPARSED.equal?(string = parse_string)
             skip(IGNORE)
             unless scan(PAIR_DELIMITER)
               raise ParserError, "expected ':' in object at '#{peek(20)}'!"
             end
             skip(IGNORE)
-            unless (value = parse_value).equal? UNPARSED
+            unless UNPARSED.equal?(value = parse_value)
               result[@symbolize_names ? string.to_sym : string] = value
               delim = false
               skip(IGNORE)
diff --git a/lib/json/version.rb b/lib/json/version.rb
index 5a4013d..b65ed87 100644
--- a/lib/json/version.rb
+++ b/lib/json/version.rb
@@ -1,6 +1,7 @@
+# frozen_string_literal: false
 module JSON
   # JSON version
-  VERSION         = '1.8.3'
+  VERSION         = '2.1.0'
   VERSION_ARRAY   = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
   VERSION_MAJOR   = VERSION_ARRAY[0] # :nodoc:
   VERSION_MINOR   = VERSION_ARRAY[1] # :nodoc:
diff --git a/metadata.yml b/metadata.yml
deleted file mode 100644
index 363a307..0000000
--- a/metadata.yml
+++ /dev/null
@@ -1,203 +0,0 @@
---- !ruby/object:Gem::Specification
-name: json
-version: !ruby/object:Gem::Version
-  version: 1.8.3
-platform: ruby
-authors:
-- Florian Frank
-autorequire: 
-bindir: bin
-cert_chain: []
-date: 2015-06-01 00:00:00.000000000 Z
-dependencies:
-- !ruby/object:Gem::Dependency
-  name: permutation
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: sdoc
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: 0.3.16
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: 0.3.16
-description: This is a JSON implementation as a Ruby extension in C.
-email: flori at ping.de
-executables: []
-extensions:
-- ext/json/ext/generator/extconf.rb
-- ext/json/ext/parser/extconf.rb
-- ext/json/extconf.rb
-extra_rdoc_files:
-- README.rdoc
-files:
-- "./tests/test_json.rb"
-- "./tests/test_json_addition.rb"
-- "./tests/test_json_encoding.rb"
-- "./tests/test_json_fixtures.rb"
-- "./tests/test_json_generate.rb"
-- "./tests/test_json_generic_object.rb"
-- "./tests/test_json_string_matching.rb"
-- "./tests/test_json_unicode.rb"
-- ".gitignore"
-- ".travis.yml"
-- CHANGES
-- COPYING
-- COPYING-json-jruby
-- GPL
-- Gemfile
-- README-json-jruby.markdown
-- README.rdoc
-- Rakefile
-- TODO
-- VERSION
-- data/example.json
-- data/index.html
-- data/prototype.js
-- diagrams/.keep
-- 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
-- install.rb
-- java/src/json/ext/ByteListTranscoder.java
-- java/src/json/ext/Generator.java
-- java/src/json/ext/GeneratorMethods.java
-- java/src/json/ext/GeneratorService.java
-- java/src/json/ext/GeneratorState.java
-- java/src/json/ext/OptionsReader.java
-- java/src/json/ext/Parser.java
-- java/src/json/ext/Parser.rl
-- java/src/json/ext/ParserService.java
-- java/src/json/ext/RuntimeInfo.java
-- java/src/json/ext/StringDecoder.java
-- java/src/json/ext/StringEncoder.java
-- java/src/json/ext/Utils.java
-- json-java.gemspec
-- json.gemspec
-- json_pure.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/struct.rb
-- lib/json/add/symbol.rb
-- lib/json/add/time.rb
-- lib/json/common.rb
-- lib/json/ext.rb
-- lib/json/ext/.keep
-- lib/json/generic_object.rb
-- lib/json/pure.rb
-- lib/json/pure/generator.rb
-- lib/json/pure/parser.rb
-- lib/json/version.rb
-- tests/fixtures/fail1.json
-- tests/fixtures/fail10.json
-- tests/fixtures/fail11.json
-- tests/fixtures/fail12.json
-- tests/fixtures/fail13.json
-- tests/fixtures/fail14.json
-- tests/fixtures/fail18.json
-- tests/fixtures/fail19.json
-- tests/fixtures/fail2.json
-- tests/fixtures/fail20.json
-- tests/fixtures/fail21.json
-- tests/fixtures/fail22.json
-- tests/fixtures/fail23.json
-- tests/fixtures/fail24.json
-- tests/fixtures/fail25.json
-- tests/fixtures/fail27.json
-- tests/fixtures/fail28.json
-- tests/fixtures/fail3.json
-- tests/fixtures/fail4.json
-- tests/fixtures/fail5.json
-- tests/fixtures/fail6.json
-- tests/fixtures/fail7.json
-- tests/fixtures/fail8.json
-- tests/fixtures/fail9.json
-- tests/fixtures/pass1.json
-- tests/fixtures/pass15.json
-- tests/fixtures/pass16.json
-- tests/fixtures/pass17.json
-- tests/fixtures/pass2.json
-- tests/fixtures/pass26.json
-- tests/fixtures/pass3.json
-- tests/setup_variant.rb
-- tests/test_json.rb
-- tests/test_json_addition.rb
-- tests/test_json_encoding.rb
-- tests/test_json_fixtures.rb
-- tests/test_json_generate.rb
-- tests/test_json_generic_object.rb
-- tests/test_json_string_matching.rb
-- tests/test_json_unicode.rb
-- tools/fuzz.rb
-- tools/server.rb
-homepage: http://flori.github.com/json
-licenses:
-- Ruby
-metadata: {}
-post_install_message: 
-rdoc_options:
-- "--title"
-- JSON implemention for Ruby
-- "--main"
-- README.rdoc
-require_paths:
-- lib
-required_ruby_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-required_rubygems_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-requirements: []
-rubyforge_project: 
-rubygems_version: 2.4.6
-signing_key: 
-specification_version: 4
-summary: JSON Implementation for Ruby
-test_files:
-- "./tests/test_json.rb"
-- "./tests/test_json_addition.rb"
-- "./tests/test_json_encoding.rb"
-- "./tests/test_json_fixtures.rb"
-- "./tests/test_json_generate.rb"
-- "./tests/test_json_generic_object.rb"
-- "./tests/test_json_string_matching.rb"
-- "./tests/test_json_unicode.rb"
diff --git a/references/rfc7159.txt b/references/rfc7159.txt
new file mode 100644
index 0000000..64fcada
--- /dev/null
+++ b/references/rfc7159.txt
@@ -0,0 +1,899 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF)                      T. Bray, Ed.
+Request for Comments: 7159                                  Google, Inc.
+Obsoletes: 4627, 7158                                         March 2014
+Category: Standards Track
+ISSN: 2070-1721
+
+
+     The JavaScript Object Notation (JSON) Data Interchange Format
+
+Abstract
+
+   JavaScript Object Notation (JSON) is a lightweight, text-based,
+   language-independent data interchange format.  It was derived from
+   the ECMAScript Programming Language Standard.  JSON defines a small
+   set of formatting rules for the portable representation of structured
+   data.
+
+   This document removes inconsistencies with other specifications of
+   JSON, repairs specification errors, and offers experience-based
+   interoperability guidance.
+
+Status of This Memo
+
+   This is an Internet Standards Track document.
+
+   This document is a product of the Internet Engineering Task Force
+   (IETF).  It represents the consensus of the IETF community.  It has
+   received public review and has been approved for publication by the
+   Internet Engineering Steering Group (IESG).  Further information on
+   Internet Standards is available in Section 2 of RFC 5741.
+
+   Information about the current status of this document, any errata,
+   and how to provide feedback on it may be obtained at
+   http://www.rfc-editor.org/info/rfc7159.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bray                         Standards Track                    [Page 1]
+

+RFC 7159                          JSON                        March 2014
+
+
+Copyright Notice
+
+   Copyright (c) 2014 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+   This document may contain material from IETF Documents or IETF
+   Contributions published or made publicly available before November
+   10, 2008.  The person(s) controlling the copyright in some of this
+   material may not have granted the IETF Trust the right to allow
+   modifications of such material outside the IETF Standards Process.
+   Without obtaining an adequate license from the person(s) controlling
+   the copyright in such materials, this document may not be modified
+   outside the IETF Standards Process, and derivative works of it may
+   not be created outside the IETF Standards Process, except to format
+   it for publication as an RFC or to translate it into languages other
+   than English.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bray                         Standards Track                    [Page 2]
+

+RFC 7159                          JSON                        March 2014
+
+
+Table of Contents
+
+   1. Introduction ....................................................3
+      1.1. Conventions Used in This Document ..........................4
+      1.2. Specifications of JSON .....................................4
+      1.3. Introduction to This Revision ..............................4
+   2. JSON Grammar ....................................................4
+   3. Values ..........................................................5
+   4. Objects .........................................................6
+   5. Arrays ..........................................................6
+   6. Numbers .........................................................6
+   7. Strings .........................................................8
+   8. String and Character Issues .....................................9
+      8.1. Character Encoding .........................................9
+      8.2. Unicode Characters .........................................9
+      8.3. String Comparison ..........................................9
+   9. Parsers ........................................................10
+   10. Generators ....................................................10
+   11. IANA Considerations ...........................................10
+   12. Security Considerations .......................................11
+   13. Examples ......................................................12
+   14. Contributors ..................................................13
+   15. References ....................................................13
+      15.1. Normative References .....................................13
+      15.2. Informative References ...................................13
+   Appendix A. Changes from RFC 4627 .................................15
+
+1.  Introduction
+
+   JavaScript Object Notation (JSON) is a text format for the
+   serialization of structured data.  It is derived from the object
+   literals of JavaScript, as defined in the ECMAScript Programming
+   Language Standard, Third Edition [ECMA-262].
+
+   JSON can represent four primitive types (strings, numbers, booleans,
+   and null) and two structured types (objects and arrays).
+
+   A string is a sequence of zero or more Unicode characters [UNICODE].
+   Note that this citation references the latest version of Unicode
+   rather than a specific release.  It is not expected that future
+   changes in the UNICODE specification will impact the syntax of JSON.
+
+   An object is an unordered collection of zero or more name/value
+   pairs, where a name is a string and a value is a string, number,
+   boolean, null, object, or array.
+
+   An array is an ordered sequence of zero or more values.
+
+
+
+
+Bray                         Standards Track                    [Page 3]
+

+RFC 7159                          JSON                        March 2014
+
+
+   The terms "object" and "array" come from the conventions of
+   JavaScript.
+
+   JSON's design goals were for it to be minimal, portable, textual, and
+   a subset of JavaScript.
+
+1.1.  Conventions Used in This Document
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in [RFC2119].
+
+   The grammatical rules in this document are to be interpreted as
+   described in [RFC5234].
+
+1.2.  Specifications of JSON
+
+   This document updates [RFC4627], which describes JSON and registers
+   the media type "application/json".
+
+   A description of JSON in ECMAScript terms appears in Version 5.1 of
+   the ECMAScript specification [ECMA-262], Section 15.12.  JSON is also
+   described in [ECMA-404].
+
+   All of the specifications of JSON syntax agree on the syntactic
+   elements of the language.
+
+1.3.  Introduction to This Revision
+
+   In the years since the publication of RFC 4627, JSON has found very
+   wide use.  This experience has revealed certain patterns, which,
+   while allowed by its specifications, have caused interoperability
+   problems.
+
+   Also, a small number of errata have been reported (see RFC Errata IDs
+   607 [Err607] and 3607 [Err3607]).
+
+   This document's goal is to apply the errata, remove inconsistencies
+   with other specifications of JSON, and highlight practices that can
+   lead to interoperability problems.
+
+2.  JSON Grammar
+
+   A JSON text is a sequence of tokens.  The set of tokens includes six
+   structural characters, strings, numbers, and three literal names.
+
+   A JSON text is a serialized value.  Note that certain previous
+   specifications of JSON constrained a JSON text to be an object or an
+
+
+
+Bray                         Standards Track                    [Page 4]
+

+RFC 7159                          JSON                        March 2014
+
+
+   array.  Implementations that generate only objects or arrays where a
+   JSON text is called for will be interoperable in the sense that all
+   implementations will accept these as conforming JSON texts.
+
+      JSON-text = ws value ws
+
+   These are the six structural characters:
+
+      begin-array     = ws %x5B ws  ; [ left square bracket
+
+      begin-object    = ws %x7B ws  ; { left curly bracket
+
+      end-array       = ws %x5D ws  ; ] right square bracket
+
+      end-object      = ws %x7D ws  ; } right curly bracket
+
+      name-separator  = ws %x3A ws  ; : colon
+
+      value-separator = ws %x2C ws  ; , comma
+
+   Insignificant whitespace is allowed before or after any of the six
+   structural characters.
+
+      ws = *(
+              %x20 /              ; Space
+              %x09 /              ; Horizontal tab
+              %x0A /              ; Line feed or New line
+              %x0D )              ; Carriage return
+
+3.  Values
+
+   A JSON value MUST be an object, array, number, or string, or one of
+   the following three literal names:
+
+      false null true
+
+   The literal names MUST be lowercase.  No other literal names are
+   allowed.
+
+      value = false / null / true / object / array / number / string
+
+      false = %x66.61.6c.73.65   ; false
+
+      null  = %x6e.75.6c.6c      ; null
+
+      true  = %x74.72.75.65      ; true
+
+
+
+
+
+Bray                         Standards Track                    [Page 5]
+

+RFC 7159                          JSON                        March 2014
+
+
+4.  Objects
+
+   An object structure is represented as a pair of curly brackets
+   surrounding zero or more name/value pairs (or members).  A name is a
+   string.  A single colon comes after each name, separating the name
+   from the value.  A single comma separates a value from a following
+   name.  The names within an object SHOULD be unique.
+
+      object = begin-object [ member *( value-separator member ) ]
+               end-object
+
+      member = string name-separator value
+
+   An object whose names are all unique is interoperable in the sense
+   that all software implementations receiving that object will agree on
+   the name-value mappings.  When the names within an object are not
+   unique, the behavior of software that receives such an object is
+   unpredictable.  Many implementations report the last name/value pair
+   only.  Other implementations report an error or fail to parse the
+   object, and some implementations report all of the name/value pairs,
+   including duplicates.
+
+   JSON parsing libraries have been observed to differ as to whether or
+   not they make the ordering of object members visible to calling
+   software.  Implementations whose behavior does not depend on member
+   ordering will be interoperable in the sense that they will not be
+   affected by these differences.
+
+5.  Arrays
+
+   An array structure is represented as square brackets surrounding zero
+   or more values (or elements).  Elements are separated by commas.
+
+   array = begin-array [ value *( value-separator value ) ] end-array
+
+   There is no requirement that the values in an array be of the same
+   type.
+
+6.  Numbers
+
+   The representation of numbers is similar to that used in most
+   programming languages.  A number is represented in base 10 using
+   decimal digits.  It contains an integer component that may be
+   prefixed with an optional minus sign, which may be followed by a
+   fraction part and/or an exponent part.  Leading zeros are not
+   allowed.
+
+   A fraction part is a decimal point followed by one or more digits.
+
+
+
+Bray                         Standards Track                    [Page 6]
+

+RFC 7159                          JSON                        March 2014
+
+
+   An exponent part begins with the letter E in upper or lower case,
+   which may be followed by a plus or minus sign.  The E and optional
+   sign are followed by one or more digits.
+
+   Numeric values that cannot be represented in the grammar below (such
+   as Infinity and NaN) are not permitted.
+
+      number = [ minus ] int [ frac ] [ exp ]
+
+      decimal-point = %x2E       ; .
+
+      digit1-9 = %x31-39         ; 1-9
+
+      e = %x65 / %x45            ; e E
+
+      exp = e [ minus / plus ] 1*DIGIT
+
+      frac = decimal-point 1*DIGIT
+
+      int = zero / ( digit1-9 *DIGIT )
+
+      minus = %x2D               ; -
+
+      plus = %x2B                ; +
+
+      zero = %x30                ; 0
+
+   This specification allows implementations to set limits on the range
+   and precision of numbers accepted.  Since software that implements
+   IEEE 754-2008 binary64 (double precision) numbers [IEEE754] is
+   generally available and widely used, good interoperability can be
+   achieved by implementations that expect no more precision or range
+   than these provide, in the sense that implementations will
+   approximate JSON numbers within the expected precision.  A JSON
+   number such as 1E400 or 3.141592653589793238462643383279 may indicate
+   potential interoperability problems, since it suggests that the
+   software that created it expects receiving software to have greater
+   capabilities for numeric magnitude and precision than is widely
+   available.
+
+   Note that when such software is used, numbers that are integers and
+   are in the range [-(2**53)+1, (2**53)-1] are interoperable in the
+   sense that implementations will agree exactly on their numeric
+   values.
+
+
+
+
+
+
+
+Bray                         Standards Track                    [Page 7]
+

+RFC 7159                          JSON                        March 2014
+
+
+7.  Strings
+
+   The representation of strings is similar to conventions used in the C
+   family of programming languages.  A string begins and ends with
+   quotation marks.  All Unicode characters may be placed within the
+   quotation marks, except for the characters that must be escaped:
+   quotation mark, reverse solidus, and the control characters (U+0000
+   through U+001F).
+
+   Any character may be escaped.  If the character is in the Basic
+   Multilingual Plane (U+0000 through U+FFFF), then it may be
+   represented as a six-character sequence: a reverse solidus, followed
+   by the lowercase letter u, followed by four hexadecimal digits that
+   encode the character's code point.  The hexadecimal letters A though
+   F can be upper or lower case.  So, for example, a string containing
+   only a single reverse solidus character may be represented as
+   "\u005C".
+
+   Alternatively, there are two-character sequence escape
+   representations of some popular characters.  So, for example, a
+   string containing only a single reverse solidus character may be
+   represented more compactly as "\\".
+
+   To escape an extended character that is not in the Basic Multilingual
+   Plane, the character is represented as a 12-character sequence,
+   encoding the UTF-16 surrogate pair.  So, for example, a string
+   containing only the G clef character (U+1D11E) may be represented as
+   "\uD834\uDD1E".
+
+      string = quotation-mark *char quotation-mark
+
+      char = unescaped /
+          escape (
+              %x22 /          ; "    quotation mark  U+0022
+              %x5C /          ; \    reverse solidus U+005C
+              %x2F /          ; /    solidus         U+002F
+              %x62 /          ; b    backspace       U+0008
+              %x66 /          ; f    form feed       U+000C
+              %x6E /          ; n    line feed       U+000A
+              %x72 /          ; r    carriage return U+000D
+              %x74 /          ; t    tab             U+0009
+              %x75 4HEXDIG )  ; uXXXX                U+XXXX
+
+      escape = %x5C              ; \
+
+      quotation-mark = %x22      ; "
+
+      unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+
+
+
+Bray                         Standards Track                    [Page 8]
+

+RFC 7159                          JSON                        March 2014
+
+
+8.  String and Character Issues
+
+8.1.  Character Encoding
+
+   JSON text SHALL be encoded in UTF-8, UTF-16, or UTF-32.  The default
+   encoding is UTF-8, and JSON texts that are encoded in UTF-8 are
+   interoperable in the sense that they will be read successfully by the
+   maximum number of implementations; there are many implementations
+   that cannot successfully read texts in other encodings (such as
+   UTF-16 and UTF-32).
+
+   Implementations MUST NOT add a byte order mark to the beginning of a
+   JSON text.  In the interests of interoperability, implementations
+   that parse JSON texts MAY ignore the presence of a byte order mark
+   rather than treating it as an error.
+
+8.2.  Unicode Characters
+
+   When all the strings represented in a JSON text are composed entirely
+   of Unicode characters [UNICODE] (however escaped), then that JSON
+   text is interoperable in the sense that all software implementations
+   that parse it will agree on the contents of names and of string
+   values in objects and arrays.
+
+   However, the ABNF in this specification allows member names and
+   string values to contain bit sequences that cannot encode Unicode
+   characters; for example, "\uDEAD" (a single unpaired UTF-16
+   surrogate).  Instances of this have been observed, for example, when
+   a library truncates a UTF-16 string without checking whether the
+   truncation split a surrogate pair.  The behavior of software that
+   receives JSON texts containing such values is unpredictable; for
+   example, implementations might return different values for the length
+   of a string value or even suffer fatal runtime exceptions.
+
+8.3.  String Comparison
+
+   Software implementations are typically required to test names of
+   object members for equality.  Implementations that transform the
+   textual representation into sequences of Unicode code units and then
+   perform the comparison numerically, code unit by code unit, are
+   interoperable in the sense that implementations will agree in all
+   cases on equality or inequality of two strings.  For example,
+   implementations that compare strings with escaped characters
+   unconverted may incorrectly find that "a\\b" and "a\u005Cb" are not
+   equal.
+
+
+
+
+
+
+Bray                         Standards Track                    [Page 9]
+

+RFC 7159                          JSON                        March 2014
+
+
+9.  Parsers
+
+   A JSON parser transforms a JSON text into another representation.  A
+   JSON parser MUST accept all texts that conform to the JSON grammar.
+   A JSON parser MAY accept non-JSON forms or extensions.
+
+   An implementation may set limits on the size of texts that it
+   accepts.  An implementation may set limits on the maximum depth of
+   nesting.  An implementation may set limits on the range and precision
+   of numbers.  An implementation may set limits on the length and
+   character contents of strings.
+
+10.  Generators
+
+   A JSON generator produces JSON text.  The resulting text MUST
+   strictly conform to the JSON grammar.
+
+11.  IANA Considerations
+
+   The MIME media type for JSON text is application/json.
+
+   Type name:  application
+
+   Subtype name:  json
+
+   Required parameters:  n/a
+
+   Optional parameters:  n/a
+
+   Encoding considerations:  binary
+
+   Security considerations:  See [RFC7159], Section 12.
+
+   Interoperability considerations:  Described in [RFC7159]
+
+   Published specification:  [RFC7159]
+
+   Applications that use this media type:
+      JSON has been used to exchange data between applications written
+      in all of these programming languages: ActionScript, C, C#,
+      Clojure, ColdFusion, Common Lisp, E, Erlang, Go, Java, JavaScript,
+      Lua, Objective CAML, Perl, PHP, Python, Rebol, Ruby, Scala, and
+      Scheme.
+
+
+
+
+
+
+
+
+Bray                         Standards Track                   [Page 10]
+

+RFC 7159                          JSON                        March 2014
+
+
+   Additional information:
+      Magic number(s): n/a
+      File extension(s): .json
+      Macintosh file type code(s): TEXT
+
+   Person & email address to contact for further information:
+      IESG
+      <iesg at ietf.org>
+
+   Intended usage:  COMMON
+
+   Restrictions on usage:  none
+
+   Author:
+      Douglas Crockford
+      <douglas at crockford.com>
+
+   Change controller:
+      IESG
+      <iesg at ietf.org>
+
+   Note:  No "charset" parameter is defined for this registration.
+      Adding one really has no effect on compliant recipients.
+
+12.  Security Considerations
+
+   Generally, there are security issues with scripting languages.  JSON
+   is a subset of JavaScript but excludes assignment and invocation.
+
+   Since JSON's syntax is borrowed from JavaScript, it is possible to
+   use that language's "eval()" function to parse JSON texts.  This
+   generally constitutes an unacceptable security risk, since the text
+   could contain executable code along with data declarations.  The same
+   consideration applies to the use of eval()-like functions in any
+   other programming language in which JSON texts conform to that
+   language's syntax.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bray                         Standards Track                   [Page 11]
+

+RFC 7159                          JSON                        March 2014
+
+
+13.  Examples
+
+   This is a JSON object:
+
+      {
+        "Image": {
+            "Width":  800,
+            "Height": 600,
+            "Title":  "View from 15th Floor",
+            "Thumbnail": {
+                "Url":    "http://www.example.com/image/481989943",
+                "Height": 125,
+                "Width":  100
+            },
+            "Animated" : false,
+            "IDs": [116, 943, 234, 38793]
+          }
+      }
+
+   Its Image member is an object whose Thumbnail member is an object and
+   whose IDs member is an array of numbers.
+
+   This is a JSON array containing two objects:
+
+      [
+        {
+           "precision": "zip",
+           "Latitude":  37.7668,
+           "Longitude": -122.3959,
+           "Address":   "",
+           "City":      "SAN FRANCISCO",
+           "State":     "CA",
+           "Zip":       "94107",
+           "Country":   "US"
+        },
+        {
+           "precision": "zip",
+           "Latitude":  37.371991,
+           "Longitude": -122.026020,
+           "Address":   "",
+           "City":      "SUNNYVALE",
+           "State":     "CA",
+           "Zip":       "94085",
+           "Country":   "US"
+        }
+      ]
+
+
+
+
+
+Bray                         Standards Track                   [Page 12]
+

+RFC 7159                          JSON                        March 2014
+
+
+   Here are three small JSON texts containing only values:
+
+   "Hello world!"
+
+   42
+
+   true
+
+14.  Contributors
+
+   RFC 4627 was written by Douglas Crockford.  This document was
+   constructed by making a relatively small number of changes to that
+   document; thus, the vast majority of the text here is his.
+
+15.  References
+
+15.1.  Normative References
+
+   [IEEE754]  IEEE, "IEEE Standard for Floating-Point Arithmetic", IEEE
+              Standard 754, August 2008,
+              <http://grouper.ieee.org/groups/754/>.
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [RFC5234]  Crocker, D. and P. Overell, "Augmented BNF for Syntax
+              Specifications: ABNF", STD 68, RFC 5234, January 2008.
+
+   [UNICODE]  The Unicode Consortium, "The Unicode Standard",
+              <http://www.unicode.org/versions/latest/>.
+
+15.2.  Informative References
+
+   [ECMA-262] Ecma International, "ECMAScript Language Specification
+              Edition 5.1", Standard ECMA-262, June 2011,
+              <http://www.ecma-international.org/publications/standards/
+              Ecma-262.htm>.
+
+   [ECMA-404] Ecma International, "The JSON Data Interchange Format",
+              Standard ECMA-404, October 2013,
+              <http://www.ecma-international.org/publications/standards/
+              Ecma-404.htm>.
+
+   [Err3607]  RFC Errata, Errata ID 3607, RFC 3607,
+              <http://www.rfc-editor.org>.
+
+
+
+
+
+
+Bray                         Standards Track                   [Page 13]
+

+RFC 7159                          JSON                        March 2014
+
+
+   [Err607]   RFC Errata, Errata ID 607, RFC 607,
+              <http://www.rfc-editor.org>.
+
+   [RFC4627]  Crockford, D., "The application/json Media Type for
+              JavaScript Object Notation (JSON)", RFC 4627, July 2006.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bray                         Standards Track                   [Page 14]
+

+RFC 7159                          JSON                        March 2014
+
+
+Appendix A.  Changes from RFC 4627
+
+   This section lists changes between this document and the text in RFC
+   4627.
+
+   o  Changed the title and abstract of the document.
+
+   o  Changed the reference to [UNICODE] to be not version specific.
+
+   o  Added a "Specifications of JSON" section.
+
+   o  Added an "Introduction to This Revision" section.
+
+   o  Changed the definition of "JSON text" so that it can be any JSON
+      value, removing the constraint that it be an object or array.
+
+   o  Added language about duplicate object member names, member
+      ordering, and interoperability.
+
+   o  Clarified the absence of a requirement that values in an array be
+      of the same JSON type.
+
+   o  Applied erratum #607 from RFC 4627 to correctly align the artwork
+      for the definition of "object".
+
+   o  Changed "as sequences of digits" to "in the grammar below" in the
+      "Numbers" section, and made base-10-ness explicit.
+
+   o  Added language about number interoperability as a function of
+      IEEE754, and added an IEEE754 reference.
+
+   o  Added language about interoperability and Unicode characters and
+      about string comparisons.  To do this, turned the old "Encoding"
+      section into a "String and Character Issues" section, with three
+      subsections: "Character Encoding", "Unicode Characters", and
+      "String Comparison".
+
+   o  Changed guidance in the "Parsers" section to point out that
+      implementations may set limits on the range "and precision" of
+      numbers.
+
+   o  Updated and tidied the "IANA Considerations" section.
+
+   o  Made a real "Security Considerations" section and lifted the text
+      out of the previous "IANA Considerations" section.
+
+
+
+
+
+
+Bray                         Standards Track                   [Page 15]
+

+RFC 7159                          JSON                        March 2014
+
+
+   o  Applied erratum #3607 from RFC 4627 by removing the security
+      consideration that begins "A JSON text can be safely passed" and
+      the JavaScript code that went with that consideration.
+
+   o  Added a note to the "Security Considerations" section pointing out
+      the risks of using the "eval()" function in JavaScript or any
+      other language in which JSON texts conform to that language's
+      syntax.
+
+   o  Added a note to the "IANA Considerations" clarifying the absence
+      of a "charset" parameter for the application/json media type.
+
+   o  Changed "100" to 100 and added a boolean field, both in the first
+      example.
+
+   o  Added examples of JSON texts with simple values, neither objects
+      nor arrays.
+
+   o  Added a "Contributors" section crediting Douglas Crockford.
+
+   o  Added a reference to RFC 4627.
+
+   o  Moved the ECMAScript reference from Normative to Informative and
+      updated it to reference ECMAScript 5.1, and added a reference to
+      ECMA 404.
+
+Author's Address
+
+   Tim Bray (editor)
+   Google, Inc.
+
+   EMail: tbray at textuality.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Bray                         Standards Track                   [Page 16]
+

diff --git a/tests/fixtures/fail1.json b/tests/fixtures/fail1.json
deleted file mode 100644
index 6216b86..0000000
--- a/tests/fixtures/fail1.json
+++ /dev/null
@@ -1 +0,0 @@
-"A JSON payload should be an object or array, not a string."
\ No newline at end of file
diff --git a/tests/fixtures/obsolete_fail1.json b/tests/fixtures/obsolete_fail1.json
new file mode 100644
index 0000000..98b77de
--- /dev/null
+++ b/tests/fixtures/obsolete_fail1.json
@@ -0,0 +1 @@
+"A JSON payload should be an object or array, not a string."
diff --git a/tests/test_json_addition.rb b/tests/json_addition_test.rb
old mode 100755
new mode 100644
similarity index 77%
rename from tests/test_json_addition.rb
rename to tests/json_addition_test.rb
index a30f06a..a028e0f
--- a/tests/test_json_addition.rb
+++ b/tests/json_addition_test.rb
@@ -1,8 +1,5 @@
-#!/usr/bin/env ruby
-# -*- coding:utf-8 -*-
-
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
+#frozen_string_literal: false
+require 'test_helper'
 require 'json/add/core'
 require 'json/add/complex'
 require 'json/add/rational'
@@ -10,7 +7,7 @@ require 'json/add/bigdecimal'
 require 'json/add/ostruct'
 require 'date'
 
-class TestJSONAddition < Test::Unit::TestCase
+class JSONAdditionTest < Test::Unit::TestCase
   include JSON
 
   class A
@@ -64,7 +61,7 @@ class TestJSONAddition < Test::Unit::TestCase
 
     def to_json(*args)
       {
-        'json_class'  => 'TestJSONAddition::Nix',
+        'json_class'  => 'JSONAdditionTest::Nix',
       }.to_json(*args)
     end
   end
@@ -73,7 +70,7 @@ class TestJSONAddition < Test::Unit::TestCase
     a = A.new(666)
     assert A.json_creatable?
     json = generate(a)
-    a_again = JSON.parse(json, :create_additions => true)
+    a_again = parse(json, :create_additions => true)
     assert_kind_of a.class, a_again
     assert_equal a, a_again
   end
@@ -82,7 +79,7 @@ class TestJSONAddition < Test::Unit::TestCase
     a = A.new(666)
     assert A.json_creatable?
     json = generate(a)
-    a_hash = JSON.parse(json)
+    a_hash = parse(json)
     assert_kind_of Hash, a_hash
   end
 
@@ -90,13 +87,13 @@ class TestJSONAddition < Test::Unit::TestCase
     a = A.new(666)
     assert A.json_creatable?
     json = generate(a)
-    a_again = JSON.parse(json, :create_additions => true)
+    a_again = parse(json, :create_additions => true)
     assert_kind_of a.class, a_again
     assert_equal a, a_again
-    a_hash = JSON.parse(json, :create_additions => false)
+    a_hash = parse(json, :create_additions => false)
     assert_kind_of Hash, a_hash
     assert_equal(
-      {"args"=>[666], "json_class"=>"TestJSONAddition::A"}.sort_by { |k,| k },
+      {"args"=>[666], "json_class"=>"JSONAdditionTest::A"}.sort_by { |k,| k },
       a_hash.sort_by { |k,| k }
     )
   end
@@ -105,14 +102,14 @@ class TestJSONAddition < Test::Unit::TestCase
     b = B.new
     assert !B.json_creatable?
     json = generate(b)
-    assert_equal({ "json_class"=>"TestJSONAddition::B" }, JSON.parse(json))
+    assert_equal({ "json_class"=>"JSONAdditionTest::B" }, parse(json))
   end
 
   def test_extended_json_fail2
     c = C.new
     assert !C.json_creatable?
     json = generate(c)
-    assert_raises(ArgumentError, NameError) { JSON.parse(json, :create_additions => true) }
+    assert_raise(ArgumentError, NameError) { parse(json, :create_additions => true) }
   end
 
   def test_raw_strings
@@ -130,7 +127,7 @@ class TestJSONAddition < Test::Unit::TestCase
     assert_match(/\A\{.*\}\z/, json)
     assert_match(/"json_class":"String"/, json)
     assert_match(/"raw":\[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, [...]
-    raw_again = JSON.parse(json, :create_additions => true)
+    raw_again = parse(json, :create_additions => true)
     assert_equal raw, raw_again
   end
 
@@ -151,7 +148,7 @@ class TestJSONAddition < Test::Unit::TestCase
     assert_equal s, JSON(JSON(s), :create_additions => true)
     struct = Struct.new :foo, :bar
     s = struct.new 4711, 'foot'
-    assert_raises(JSONError) { JSON(s) }
+    assert_raise(JSONError) { JSON(s) }
     begin
       raise TypeError, "test me"
     rescue TypeError => e
@@ -167,19 +164,19 @@ class TestJSONAddition < Test::Unit::TestCase
 
   def test_utc_datetime
     now = Time.now
-    d = DateTime.parse(now.to_s, :create_additions => true)                    # usual case
-    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
-    d = DateTime.parse(now.utc.to_s)                # of = 0
-    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+    d = DateTime.parse(now.to_s, :create_additions => true) # usual case
+    assert_equal d, parse(d.to_json, :create_additions => true)
+    d = DateTime.parse(now.utc.to_s) # of = 0
+    assert_equal d, parse(d.to_json, :create_additions => true)
     d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(1,24))
-    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+    assert_equal d, parse(d.to_json, :create_additions => true)
     d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(12,24))
-    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+    assert_equal d, parse(d.to_json, :create_additions => true)
   end
 
   def test_rational_complex
-    assert_equal Rational(2, 9), JSON.parse(JSON(Rational(2, 9)), :create_additions => true)
-    assert_equal Complex(2, 9), JSON.parse(JSON(Complex(2, 9)), :create_additions => true)
+    assert_equal Rational(2, 9), parse(JSON(Rational(2, 9)), :create_additions => true)
+    assert_equal Complex(2, 9), parse(JSON(Complex(2, 9)), :create_additions => true)
   end
 
   def test_bigdecimal
@@ -191,6 +188,6 @@ class TestJSONAddition < Test::Unit::TestCase
     o = OpenStruct.new
     # XXX this won't work; o.foo = { :bar => true }
     o.foo = { 'bar' => true }
-    assert_equal o, JSON.parse(JSON(o), :create_additions => true)
+    assert_equal o, parse(JSON(o), :create_additions => true)
   end
 end
diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb
new file mode 100644
index 0000000..29b4a5b
--- /dev/null
+++ b/tests/json_common_interface_test.rb
@@ -0,0 +1,126 @@
+#frozen_string_literal: false
+require 'test_helper'
+require 'stringio'
+require 'tempfile'
+
+class JSONCommonInterfaceTest < Test::Unit::TestCase
+  include JSON
+
+  def setup
+    @hash = {
+      'a' => 2,
+      'b' => 3.141,
+      'c' => 'c',
+      'd' => [ 1, "b", 3.14 ],
+      'e' => { 'foo' => 'bar' },
+      'g' => "\"\0\037",
+      'h' => 1000.0,
+      'i' => 0.001
+    }
+    @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
+      '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
+  end
+
+  def test_index
+    assert_equal @json, JSON[@hash]
+    assert_equal @hash, JSON[@json]
+  end
+
+  def test_parser
+    assert_match /::Parser\z/, JSON.parser.name
+  end
+
+  def test_generator
+    assert_match /::Generator\z/, JSON.generator.name
+  end
+
+  def test_state
+    assert_match /::Generator::State\z/, JSON.state.name
+  end
+
+  def test_create_id
+    assert_equal 'json_class', JSON.create_id
+    JSON.create_id = 'foo_bar'
+    assert_equal 'foo_bar', JSON.create_id
+  ensure
+    JSON.create_id = 'json_class'
+  end
+
+  def test_deep_const_get
+    assert_raise(ArgumentError) { JSON.deep_const_get('Nix::Da') }
+    assert_equal File::SEPARATOR, JSON.deep_const_get('File::SEPARATOR')
+  end
+
+  def test_parse
+    assert_equal [ 1, 2, 3, ], JSON.parse('[ 1, 2, 3 ]')
+  end
+
+  def test_parse_bang
+    assert_equal [ 1, NaN, 3, ], JSON.parse!('[ 1, NaN, 3 ]')
+  end
+
+  def test_generate
+    assert_equal '[1,2,3]', JSON.generate([ 1, 2, 3 ])
+  end
+
+  def test_fast_generate
+    assert_equal '[1,2,3]', JSON.generate([ 1, 2, 3 ])
+  end
+
+  def test_pretty_generate
+    assert_equal "[\n  1,\n  2,\n  3\n]", JSON.pretty_generate([ 1, 2, 3 ])
+  end
+
+  def test_load
+    assert_equal @hash, JSON.load(@json)
+    tempfile = Tempfile.open('@json')
+    tempfile.write @json
+    tempfile.rewind
+    assert_equal @hash, JSON.load(tempfile)
+    stringio = StringIO.new(@json)
+    stringio.rewind
+    assert_equal @hash, JSON.load(stringio)
+    assert_equal nil, JSON.load(nil)
+    assert_equal nil, JSON.load('')
+  ensure
+    tempfile.close!
+  end
+
+  def test_load_with_options
+    json  = '{ "foo": NaN }'
+    assert JSON.load(json, nil, :allow_nan => true)['foo'].nan?
+  end
+
+  def test_load_null
+    assert_equal nil, JSON.load(nil, nil, :allow_blank => true)
+    assert_raise(TypeError) { JSON.load(nil, nil, :allow_blank => false) }
+    assert_raise(JSON::ParserError) { JSON.load('', nil, :allow_blank => false) }
+  end
+
+  def test_dump
+    too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
+    assert_equal too_deep, dump(eval(too_deep))
+    assert_kind_of String, Marshal.dump(eval(too_deep))
+    assert_raise(ArgumentError) { dump(eval(too_deep), 100) }
+    assert_raise(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
+    assert_equal too_deep, dump(eval(too_deep), 101)
+    assert_kind_of String, Marshal.dump(eval(too_deep), 101)
+    output = StringIO.new
+    dump(eval(too_deep), output)
+    assert_equal too_deep, output.string
+    output = StringIO.new
+    dump(eval(too_deep), output, 101)
+    assert_equal too_deep, output.string
+  end
+
+  def test_dump_should_modify_defaults
+    max_nesting = JSON.dump_default_options[:max_nesting]
+    dump([], StringIO.new, 10)
+    assert_equal max_nesting, JSON.dump_default_options[:max_nesting]
+  end
+
+  def test_JSON
+    assert_equal @json, JSON(@hash)
+    assert_equal @hash, JSON(@json)
+  end
+end
diff --git a/tests/json_encoding_test.rb b/tests/json_encoding_test.rb
new file mode 100644
index 0000000..cc7b715
--- /dev/null
+++ b/tests/json_encoding_test.rb
@@ -0,0 +1,107 @@
+# encoding: utf-8
+#frozen_string_literal: false
+require 'test_helper'
+
+class JSONEncodingTest < Test::Unit::TestCase
+  include JSON
+
+  def setup
+    @utf_8      = '"© ≠ €!"'
+    @ascii_8bit = @utf_8.dup.force_encoding('ascii-8bit')
+    @parsed     = "© ≠ €!"
+    @generated  = '"\u00a9 \u2260 \u20ac!"'
+    if String.method_defined?(:encode)
+      @utf_16_data = @parsed.encode('utf-16be', 'utf-8')
+      @utf_16be = @utf_8.encode('utf-16be', 'utf-8')
+      @utf_16le = @utf_8.encode('utf-16le', 'utf-8')
+      @utf_32be = @utf_8.encode('utf-32be', 'utf-8')
+      @utf_32le = @utf_8.encode('utf-32le', 'utf-8')
+    else
+      require 'iconv'
+      @utf_16_data, = Iconv.iconv('utf-16be', 'utf-8', @parsed)
+      @utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8)
+      @utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8)
+      @utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8)
+      @utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8)
+    end
+  end
+
+  def test_parse
+    assert_equal @parsed, JSON.parse(@ascii_8bit)
+    assert_equal @parsed, JSON.parse(@utf_8)
+    assert_equal @parsed, JSON.parse(@utf_16be)
+    assert_equal @parsed, JSON.parse(@utf_16le)
+    assert_equal @parsed, JSON.parse(@utf_32be)
+    assert_equal @parsed, JSON.parse(@utf_32le)
+  end
+
+  def test_generate
+    assert_equal @generated, JSON.generate(@parsed, :ascii_only => true)
+    assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true)
+  end
+
+  def test_unicode
+    assert_equal '""', ''.to_json
+    assert_equal '"\\b"', "\b".to_json
+    assert_equal '"\u0001"', 0x1.chr.to_json
+    assert_equal '"\u001f"', 0x1f.chr.to_json
+    assert_equal '" "', ' '.to_json
+    assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
+    utf8 = [ "© ≠ €! \01" ]
+    json = '["© ≠ €! \u0001"]'
+    assert_equal json, utf8.to_json(:ascii_only => false)
+    assert_equal utf8, parse(json)
+    json = '["\u00a9 \u2260 \u20ac! \u0001"]'
+    assert_equal json, utf8.to_json(:ascii_only => true)
+    assert_equal utf8, parse(json)
+    utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
+    json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]"
+    assert_equal utf8, parse(json)
+    assert_equal json, utf8.to_json(:ascii_only => false)
+    utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
+    assert_equal utf8, parse(json)
+    json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
+    assert_equal json, utf8.to_json(:ascii_only => true)
+    assert_equal utf8, parse(json)
+    utf8 = ['საქართველო']
+    json = '["საქართველო"]'
+    assert_equal json, utf8.to_json(:ascii_only => false)
+    json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
+    assert_equal json, utf8.to_json(:ascii_only => true)
+    assert_equal utf8, parse(json)
+    assert_equal '["Ã"]', generate(["Ã"], :ascii_only => false)
+    assert_equal '["\\u00c3"]', generate(["Ã"], :ascii_only => true)
+    assert_equal ["€"], parse('["\u20ac"]')
+    utf8 = ["\xf0\xa0\x80\x81"]
+    json = "[\"\xf0\xa0\x80\x81\"]"
+    assert_equal json, generate(utf8, :ascii_only => false)
+    assert_equal utf8, parse(json)
+    json = '["\ud840\udc01"]'
+    assert_equal json, generate(utf8, :ascii_only => true)
+    assert_equal utf8, parse(json)
+    assert_raise(JSON::ParserError) { parse('"\u"') }
+    assert_raise(JSON::ParserError) { parse('"\ud800"') }
+  end
+
+  def test_chars
+    (0..0x7f).each do |i|
+      json = '["\u%04x"]' % i
+      if RUBY_VERSION >= "1.9."
+        i = i.chr
+      end
+      assert_equal i, parse(json).first[0]
+      if i == ?\b
+        generated = generate(["" << i])
+        assert '["\b"]' == generated || '["\10"]' == generated
+      elsif [?\n, ?\r, ?\t, ?\f].include?(i)
+        assert_equal '[' << ('' << i).dump << ']', generate(["" << i])
+      elsif i.chr < 0x20.chr
+        assert_equal json, generate(["" << i])
+      end
+    end
+    assert_raise(JSON::GeneratorError) do
+      generate(["\x80"], :ascii_only => true)
+    end
+    assert_equal "\302\200", parse('["\u0080"]').first
+  end
+end
diff --git a/tests/json_ext_parser_test.rb b/tests/json_ext_parser_test.rb
new file mode 100644
index 0000000..c5a030e
--- /dev/null
+++ b/tests/json_ext_parser_test.rb
@@ -0,0 +1,15 @@
+#frozen_string_literal: false
+require 'test_helper'
+
+class JSONExtParserTest < Test::Unit::TestCase
+  if defined?(JSON::Ext::Parser)
+    def test_allocate
+      parser = JSON::Ext::Parser.new("{}")
+      assert_raise(TypeError, '[ruby-core:35079]') do
+        parser.__send__(:initialize, "{}")
+      end
+      parser = JSON::Ext::Parser.allocate
+      assert_raise(TypeError, '[ruby-core:35079]') { parser.source }
+    end
+  end
+end
diff --git a/tests/test_json_fixtures.rb b/tests/json_fixtures_test.rb
old mode 100755
new mode 100644
similarity index 71%
rename from tests/test_json_fixtures.rb
rename to tests/json_fixtures_test.rb
index 584dffd..01954fe
--- a/tests/test_json_fixtures.rb
+++ b/tests/json_fixtures_test.rb
@@ -1,12 +1,9 @@
-#!/usr/bin/env ruby
-# encoding: utf-8
+#frozen_string_literal: false
+require 'test_helper'
 
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
-
-class TestJSONFixtures < Test::Unit::TestCase
+class JSONFixturesTest < Test::Unit::TestCase
   def setup
-    fixtures = File.join(File.dirname(__FILE__), 'fixtures/*.json')
+    fixtures = File.join(File.dirname(__FILE__), 'fixtures/{fail,pass}.json')
     passed, failed = Dir[fixtures].partition { |f| f['pass'] }
     @passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
     @failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
@@ -26,7 +23,7 @@ class TestJSONFixtures < Test::Unit::TestCase
 
   def test_failing
     for name, source in @failed
-      assert_raises(JSON::ParserError, JSON::NestingError,
+      assert_raise(JSON::ParserError, JSON::NestingError,
         "Did not fail for fixture '#{name}': #{source.inspect}") do
         JSON.parse(source)
       end
diff --git a/tests/test_json_generate.rb b/tests/json_generator_test.rb
old mode 100755
new mode 100644
similarity index 72%
rename from tests/test_json_generate.rb
rename to tests/json_generator_test.rb
index 8db0b78..86be398
--- a/tests/test_json_generate.rb
+++ b/tests/json_generator_test.rb
@@ -1,10 +1,10 @@
 #!/usr/bin/env ruby
 # encoding: utf-8
+# frozen_string_literal: false
 
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'test_helper'
 
-class TestJSONGenerate < Test::Unit::TestCase
+class JSONGeneratorTest < Test::Unit::TestCase
   include JSON
 
   def setup
@@ -42,23 +42,23 @@ EOT
 
   def test_generate
     json = generate(@hash)
-    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    assert_equal(parse(@json2), parse(json))
     json = JSON[@hash]
-    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    assert_equal(parse(@json2), parse(json))
     parsed_json = parse(json)
     assert_equal(@hash, parsed_json)
     json = generate({1=>2})
     assert_equal('{"1":2}', json)
     parsed_json = parse(json)
     assert_equal({"1"=>2}, parsed_json)
-    assert_raise(GeneratorError) { generate(666) }
-    assert_equal '666', generate(666, :quirks_mode => true)
+    assert_equal '666', generate(666)
   end
 
   def test_generate_pretty
     json = pretty_generate(@hash)
-    # hashes aren't (insertion) ordered on every ruby implementation assert_equal(@json3, json)
-    assert_equal(JSON.parse(@json3), JSON.parse(json))
+    # hashes aren't (insertion) ordered on every ruby implementation
+    # assert_equal(@json3, json)
+    assert_equal(parse(@json3), parse(json))
     parsed_json = parse(json)
     assert_equal(@hash, parsed_json)
     json = pretty_generate({1=>2})
@@ -69,8 +69,7 @@ EOT
 EOT
     parsed_json = parse(json)
     assert_equal({"1"=>2}, parsed_json)
-    assert_raise(GeneratorError) { pretty_generate(666) }
-    assert_equal '666', pretty_generate(666, :quirks_mode => true)
+    assert_equal '666', pretty_generate(666)
   end
 
   def test_generate_custom
@@ -88,30 +87,26 @@ EOT
 
   def test_fast_generate
     json = fast_generate(@hash)
-    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    assert_equal(parse(@json2), parse(json))
     parsed_json = parse(json)
     assert_equal(@hash, parsed_json)
     json = fast_generate({1=>2})
     assert_equal('{"1":2}', json)
     parsed_json = parse(json)
     assert_equal({"1"=>2}, parsed_json)
-    assert_raise(GeneratorError) { fast_generate(666) }
-    assert_equal '666', fast_generate(666, :quirks_mode => true)
+    assert_equal '666', fast_generate(666)
   end
 
   def test_own_state
     state = State.new
     json = generate(@hash, state)
-    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    assert_equal(parse(@json2), parse(json))
     parsed_json = parse(json)
     assert_equal(@hash, parsed_json)
     json = generate({1=>2}, state)
     assert_equal('{"1":2}', json)
     parsed_json = parse(json)
     assert_equal({"1"=>2}, parsed_json)
-    assert_raise(GeneratorError) { generate(666, state) }
-    state.quirks_mode = true
-    assert state.quirks_mode?
     assert_equal '666', generate(666, state)
   end
 
@@ -123,12 +118,12 @@ EOT
     assert s[:check_circular?]
     h = { 1=>2 }
     h[3] = h
-    assert_raises(JSON::NestingError) {  generate(h) }
-    assert_raises(JSON::NestingError) {  generate(h, s) }
+    assert_raise(JSON::NestingError) {  generate(h) }
+    assert_raise(JSON::NestingError) {  generate(h, s) }
     s = JSON.state.new
     a = [ 1, 2 ]
     a << a
-    assert_raises(JSON::NestingError) {  generate(a, s) }
+    assert_raise(JSON::NestingError) {  generate(a, s) }
     assert s.check_circular?
     assert s[:check_circular?]
   end
@@ -140,7 +135,6 @@ EOT
       :array_nl              => "\n",
       :ascii_only            => false,
       :buffer_initial_length => 1024,
-      :quirks_mode           => false,
       :depth                 => 0,
       :indent                => "  ",
       :max_nesting           => 100,
@@ -157,7 +151,6 @@ EOT
       :array_nl              => "",
       :ascii_only            => false,
       :buffer_initial_length => 1024,
-      :quirks_mode           => false,
       :depth                 => 0,
       :indent                => "",
       :max_nesting           => 100,
@@ -174,7 +167,6 @@ EOT
       :array_nl              => "",
       :ascii_only            => false,
       :buffer_initial_length => 1024,
-      :quirks_mode           => false,
       :depth                 => 0,
       :indent                => "",
       :max_nesting           => 0,
@@ -185,34 +177,34 @@ EOT
   end
 
   def test_allow_nan
-    assert_raises(GeneratorError) { generate([JSON::NaN]) }
+    assert_raise(GeneratorError) { generate([JSON::NaN]) }
     assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
-    assert_raises(GeneratorError) { fast_generate([JSON::NaN]) }
-    assert_raises(GeneratorError) { pretty_generate([JSON::NaN]) }
+    assert_raise(GeneratorError) { fast_generate([JSON::NaN]) }
+    assert_raise(GeneratorError) { pretty_generate([JSON::NaN]) }
     assert_equal "[\n  NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
-    assert_raises(GeneratorError) { generate([JSON::Infinity]) }
+    assert_raise(GeneratorError) { generate([JSON::Infinity]) }
     assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
-    assert_raises(GeneratorError) { fast_generate([JSON::Infinity]) }
-    assert_raises(GeneratorError) { pretty_generate([JSON::Infinity]) }
+    assert_raise(GeneratorError) { fast_generate([JSON::Infinity]) }
+    assert_raise(GeneratorError) { pretty_generate([JSON::Infinity]) }
     assert_equal "[\n  Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
-    assert_raises(GeneratorError) { generate([JSON::MinusInfinity]) }
+    assert_raise(GeneratorError) { generate([JSON::MinusInfinity]) }
     assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
-    assert_raises(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
-    assert_raises(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
+    assert_raise(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
+    assert_raise(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
     assert_equal "[\n  -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
   end
 
   def test_depth
     ary = []; ary << ary
     assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
-    assert_raises(JSON::NestingError) { JSON.generate(ary) }
+    assert_raise(JSON::NestingError) { generate(ary) }
     assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
     assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
-    assert_raises(JSON::NestingError) { JSON.pretty_generate(ary) }
+    assert_raise(JSON::NestingError) { JSON.pretty_generate(ary) }
     assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
     s = JSON.state.new
     assert_equal 0, s.depth
-    assert_raises(JSON::NestingError) { ary.to_json(s) }
+    assert_raise(JSON::NestingError) { ary.to_json(s) }
     assert_equal 100, s.depth
   end
 
@@ -285,12 +277,13 @@ EOT
   if defined?(JSON::Ext::Generator)
     def test_broken_bignum # [ruby-core:38867]
       pid = fork do
-        Bignum.class_eval do
+        x = 1 << 64
+        x.class.class_eval do
           def to_s
           end
         end
         begin
-          JSON::Ext::Generator::State.new.generate(1<<64)
+          JSON::Ext::Generator::State.new.generate(x)
           exit 1
         rescue TypeError
           exit 0
@@ -331,7 +324,54 @@ EOT
 
   def test_json_generate
     assert_raise JSON::GeneratorError do
-      assert_equal true, JSON.generate(["\xea"])
+      assert_equal true, generate(["\xea"])
+    end
+  end
+
+  def test_nesting
+    too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
+    too_deep_ary = eval too_deep
+    assert_raise(JSON::NestingError) { generate too_deep_ary }
+    assert_raise(JSON::NestingError) { generate too_deep_ary, :max_nesting => 100 }
+    ok = generate too_deep_ary, :max_nesting => 101
+    assert_equal too_deep, ok
+    ok = generate too_deep_ary, :max_nesting => nil
+    assert_equal too_deep, ok
+    ok = generate too_deep_ary, :max_nesting => false
+    assert_equal too_deep, ok
+    ok = generate too_deep_ary, :max_nesting => 0
+    assert_equal too_deep, ok
+  end
+
+  def test_backslash
+    data = [ '\\.(?i:gif|jpe?g|png)$' ]
+    json = '["\\\\.(?i:gif|jpe?g|png)$"]'
+    assert_equal json, generate(data)
+    #
+    data = [ '\\"' ]
+    json = '["\\\\\""]'
+    assert_equal json, generate(data)
+    #
+    data = [ '/' ]
+    json = '["/"]'
+    assert_equal json, generate(data)
+    #
+    data = ['"']
+    json = '["\""]'
+    assert_equal json, generate(data)
+    #
+    data = ["'"]
+    json = '["\\\'"]'
+    assert_equal '["\'"]', generate(data)
+  end
+
+  def test_string_subclass
+    s = Class.new(String) do
+      def to_s; self; end
+      undef to_json
+    end
+    assert_nothing_raised(SystemStackError) do
+      assert_equal '["foo"]', JSON.generate([s.new('foo')])
     end
   end
 end
diff --git a/tests/test_json_generic_object.rb b/tests/json_generic_object_test.rb
similarity index 79%
rename from tests/test_json_generic_object.rb
rename to tests/json_generic_object_test.rb
index c43c776..82742dc 100644
--- a/tests/test_json_generic_object.rb
+++ b/tests/json_generic_object_test.rb
@@ -1,9 +1,7 @@
-#!/usr/bin/env ruby
-# encoding: utf-8
+#frozen_string_literal: false
+require 'test_helper'
 
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
-class TestJSONGenericObject < Test::Unit::TestCase
+class JSONGenericObjectTest < Test::Unit::TestCase
   include JSON
 
   def setup
@@ -26,11 +24,20 @@ class TestJSONGenericObject < Test::Unit::TestCase
   end
 
   def test_parse_json
-    assert_kind_of Hash, JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true)
+    assert_kind_of Hash,
+      JSON(
+        '{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }',
+        :create_additions => true
+      )
     switch_json_creatable do
-      assert_equal @go, l = JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true)
+      assert_equal @go, l =
+        JSON(
+          '{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }',
+             :create_additions => true
+        )
       assert_equal 1, l.a
-      assert_equal @go, l = JSON('{ "a": 1, "b": 2 }', :object_class => GenericObject)
+      assert_equal @go,
+        l = JSON('{ "a": 1, "b": 2 }', :object_class => GenericObject)
       assert_equal 1, l.a
       assert_equal GenericObject[:a => GenericObject[:b => 2]],
         l = JSON('{ "a": { "b": 2 } }', :object_class => GenericObject)
diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb
new file mode 100644
index 0000000..f36e9c8
--- /dev/null
+++ b/tests/json_parser_test.rb
@@ -0,0 +1,471 @@
+# encoding: utf-8
+# frozen_string_literal: false
+require 'test_helper'
+require 'stringio'
+require 'tempfile'
+require 'ostruct'
+
+class JSONParserTest < Test::Unit::TestCase
+  include JSON
+
+  def test_construction
+    parser = JSON::Parser.new('test')
+    assert_equal 'test', parser.source
+  end
+
+  def test_argument_encoding
+    source = "{}".encode("UTF-16")
+    JSON::Parser.new(source)
+    assert_equal Encoding::UTF_16, source.encoding
+  end if defined?(Encoding::UTF_16)
+
+  def test_error_message_encoding
+    bug10705 = '[ruby-core:67386] [Bug #10705]'
+    json = ".\"\xE2\x88\x9A\"".force_encoding(Encoding::UTF_8)
+    e = assert_raise(JSON::ParserError) {
+      JSON::Ext::Parser.new(json).parse
+    }
+    assert_equal(Encoding::UTF_8, e.message.encoding, bug10705)
+    assert_include(e.message, json, bug10705)
+  end if defined?(Encoding::UTF_8) and defined?(JSON::Ext::Parser)
+
+  def test_parsing
+    parser = JSON::Parser.new('"test"')
+    assert_equal 'test', parser.parse
+  end
+
+  def test_parser_reset
+    parser = Parser.new('{"a":"b"}')
+    assert_equal({ 'a' => 'b' }, parser.parse)
+    assert_equal({ 'a' => 'b' }, parser.parse)
+  end
+
+  def test_parse_values
+    assert_equal(nil,      parse('null'))
+    assert_equal(false,    parse('false'))
+    assert_equal(true,     parse('true'))
+    assert_equal(-23,      parse('-23'))
+    assert_equal(23,       parse('23'))
+    assert_in_delta(0.23,  parse('0.23'), 1e-2)
+    assert_in_delta(0.0,   parse('0e0'), 1e-2)
+    assert_equal("",       parse('""'))
+    assert_equal("foobar", parse('"foobar"'))
+  end
+
+  def test_parse_simple_arrays
+    assert_equal([],             parse('[]'))
+    assert_equal([],             parse('  [  ] '))
+    assert_equal([ nil ],        parse('[null]'))
+    assert_equal([ false ],      parse('[false]'))
+    assert_equal([ true ],       parse('[true]'))
+    assert_equal([ -23 ],        parse('[-23]'))
+    assert_equal([ 23 ],         parse('[23]'))
+    assert_equal_float([ 0.23 ], parse('[0.23]'))
+    assert_equal_float([ 0.0 ],  parse('[0e0]'))
+    assert_equal([""],           parse('[""]'))
+    assert_equal(["foobar"],     parse('["foobar"]'))
+    assert_equal([{}],           parse('[{}]'))
+  end
+
+  def test_parse_simple_objects
+    assert_equal({}, parse('{}'))
+    assert_equal({}, parse(' {   }   '))
+    assert_equal({ "a" => nil }, parse('{   "a"   :  null}'))
+    assert_equal({ "a" => nil }, parse('{"a":null}'))
+    assert_equal({ "a" => false }, parse('{   "a"  :  false  }  '))
+    assert_equal({ "a" => false }, parse('{"a":false}'))
+    assert_raise(JSON::ParserError) { parse('{false}') }
+    assert_equal({ "a" => true }, parse('{"a":true}'))
+    assert_equal({ "a" => true }, parse('  { "a" :  true  }   '))
+    assert_equal({ "a" => -23 }, parse('  {  "a"  :  -23  }  '))
+    assert_equal({ "a" => -23 }, parse('  { "a" : -23 } '))
+    assert_equal({ "a" => 23 }, parse('{"a":23  } '))
+    assert_equal({ "a" => 23 }, parse('  { "a"  : 23  } '))
+    assert_equal({ "a" => 0.23 }, parse(' { "a"  :  0.23 }  '))
+    assert_equal({ "a" => 0.23 }, parse('  {  "a"  :  0.23  }  '))
+  end
+
+  def test_parse_numbers
+    assert_raise(JSON::ParserError) { parse('+23.2') }
+    assert_raise(JSON::ParserError) { parse('+23') }
+    assert_raise(JSON::ParserError) { parse('.23') }
+    assert_raise(JSON::ParserError) { parse('023') }
+    assert_equal 23, parse('23')
+    assert_equal -23, parse('-23')
+    assert_equal_float 3.141, parse('3.141')
+    assert_equal_float -3.141, parse('-3.141')
+    assert_equal_float 3.141, parse('3141e-3')
+    assert_equal_float 3.141, parse('3141.1e-3')
+    assert_equal_float 3.141, parse('3141E-3')
+    assert_equal_float 3.141, parse('3141.0E-3')
+    assert_equal_float -3.141, parse('-3141.0e-3')
+    assert_equal_float -3.141, parse('-3141e-3')
+    assert_raise(ParserError) { parse('NaN') }
+    assert parse('NaN', :allow_nan => true).nan?
+    assert_raise(ParserError) { parse('Infinity') }
+    assert_equal 1.0/0, parse('Infinity', :allow_nan => true)
+    assert_raise(ParserError) { parse('-Infinity') }
+    assert_equal -1.0/0, parse('-Infinity', :allow_nan => true)
+  end
+
+  def test_parse_bigdecimals
+    assert_equal(BigDecimal,                                 JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class)
+    assert_equal(BigDecimal.new("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"]      )
+  end
+
+  if Array.method_defined?(:permutation)
+    def test_parse_more_complex_arrays
+      a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
+      a.permutation.each do |perm|
+        json = pretty_generate(perm)
+        assert_equal perm, parse(json)
+      end
+    end
+
+    def test_parse_complex_objects
+      a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
+      a.permutation.each do |perm|
+        s = "a"
+        orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
+        json = pretty_generate(orig_obj)
+        assert_equal orig_obj, parse(json)
+      end
+    end
+  end
+
+  def test_parse_arrays
+    assert_equal([1,2,3], parse('[1,2,3]'))
+    assert_equal([1.2,2,3], parse('[1.2,2,3]'))
+    assert_equal([[],[[],[]]], parse('[[],[[],[]]]'))
+    assert_equal([], parse('[]'))
+    assert_equal([], parse('  [  ]  '))
+    assert_equal([1], parse('[1]'))
+    assert_equal([1], parse('  [ 1  ]  '))
+    ary = [[1], ["foo"], [3.14], [4711.0], [2.718], [nil],
+      [[1, -2, 3]], [false], [true]]
+    assert_equal(ary,
+      parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]],[false],[true]]'))
+    assert_equal(ary, parse(%Q{   [   [1] , ["foo"]  ,  [3.14] \t ,  [47.11e+2]\s
+      , [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ]  }))
+  end
+
+  def test_parse_json_primitive_values
+    assert_raise(JSON::ParserError) { parse('') }
+    assert_raise(TypeError) { parse(nil) }
+    assert_raise(JSON::ParserError) { parse('  /* foo */ ') }
+    assert_equal nil, parse('null')
+    assert_equal false, parse('false')
+    assert_equal true, parse('true')
+    assert_equal 23, parse('23')
+    assert_equal 1, parse('1')
+    assert_equal_float 3.141, parse('3.141'), 1E-3
+    assert_equal 2 ** 64, parse('18446744073709551616')
+    assert_equal 'foo', parse('"foo"')
+    assert parse('NaN', :allow_nan => true).nan?
+    assert parse('Infinity', :allow_nan => true).infinite?
+    assert parse('-Infinity', :allow_nan => true).infinite?
+    assert_raise(JSON::ParserError) { parse('[ 1, ]') }
+  end
+
+  def test_parse_some_strings
+    assert_equal([""], parse('[""]'))
+    assert_equal(["\\"], parse('["\\\\"]'))
+    assert_equal(['"'], parse('["\""]'))
+    assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]'))
+    assert_equal(
+      ["\"\b\n\r\t\0\037"],
+      parse('["\"\b\n\r\t\u0000\u001f"]')
+    )
+  end
+
+  def test_parse_big_integers
+    json1 = JSON(orig = (1 << 31) - 1)
+    assert_equal orig, parse(json1)
+    json2 = JSON(orig = 1 << 31)
+    assert_equal orig, parse(json2)
+    json3 = JSON(orig = (1 << 62) - 1)
+    assert_equal orig, parse(json3)
+    json4 = JSON(orig = 1 << 62)
+    assert_equal orig, parse(json4)
+    json5 = JSON(orig = 1 << 64)
+    assert_equal orig, parse(json5)
+  end
+
+  def test_some_wrong_inputs
+    assert_raise(ParserError) { parse('[] bla') }
+    assert_raise(ParserError) { parse('[] 1') }
+    assert_raise(ParserError) { parse('[] []') }
+    assert_raise(ParserError) { parse('[] {}') }
+    assert_raise(ParserError) { parse('{} []') }
+    assert_raise(ParserError) { parse('{} {}') }
+    assert_raise(ParserError) { parse('[NULL]') }
+    assert_raise(ParserError) { parse('[FALSE]') }
+    assert_raise(ParserError) { parse('[TRUE]') }
+    assert_raise(ParserError) { parse('[07]    ') }
+    assert_raise(ParserError) { parse('[0a]') }
+    assert_raise(ParserError) { parse('[1.]') }
+    assert_raise(ParserError) { parse('     ') }
+  end
+
+  def test_symbolize_names
+    assert_equal({ "foo" => "bar", "baz" => "quux" },
+      parse('{"foo":"bar", "baz":"quux"}'))
+    assert_equal({ :foo => "bar", :baz => "quux" },
+      parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true))
+    assert_raise(ArgumentError) do
+      parse('{}', :symbolize_names => true, :create_additions => true)
+    end
+  end
+
+  def test_parse_comments
+    json = <<EOT
+{
+  "key1":"value1", // eol comment
+  "key2":"value2"  /* multi line
+                    *  comment */,
+  "key3":"value3"  /* multi line
+                    // nested eol comment
+                    *  comment */
+}
+EOT
+    assert_equal(
+      { "key1" => "value1", "key2" => "value2", "key3" => "value3" },
+      parse(json))
+    json = <<EOT
+{
+  "key1":"value1"  /* multi line
+                    // nested eol comment
+                    /* illegal nested multi line comment */
+                    *  comment */
+}
+EOT
+    assert_raise(ParserError) { parse(json) }
+    json = <<EOT
+{
+  "key1":"value1"  /* multi line
+                   // nested eol comment
+                   closed multi comment */
+                   and again, throw an Error */
+}
+EOT
+    assert_raise(ParserError) { parse(json) }
+    json = <<EOT
+{
+  "key1":"value1"  /*/*/
+}
+EOT
+    assert_equal({ "key1" => "value1" }, parse(json))
+  end
+
+  def test_nesting
+    too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
+    too_deep_ary = eval too_deep
+    assert_raise(JSON::NestingError) { parse too_deep }
+    assert_raise(JSON::NestingError) { parse too_deep, :max_nesting => 100 }
+    ok = parse too_deep, :max_nesting => 101
+    assert_equal too_deep_ary, ok
+    ok = parse too_deep, :max_nesting => nil
+    assert_equal too_deep_ary, ok
+    ok = parse too_deep, :max_nesting => false
+    assert_equal too_deep_ary, ok
+    ok = parse too_deep, :max_nesting => 0
+    assert_equal too_deep_ary, ok
+  end
+
+  def test_backslash
+    data = [ '\\.(?i:gif|jpe?g|png)$' ]
+    json = '["\\\\.(?i:gif|jpe?g|png)$"]'
+    assert_equal data, parse(json)
+    #
+    data = [ '\\"' ]
+    json = '["\\\\\""]'
+    assert_equal data, parse(json)
+    #
+    json = '["/"]'
+    data = [ '/' ]
+    assert_equal data, parse(json)
+    #
+    json = '["\""]'
+    data = ['"']
+    assert_equal data, parse(json)
+    #
+    json = '["\\\'"]'
+    data = ["'"]
+    assert_equal data, parse(json)
+  end
+
+  class SubArray < Array
+    def <<(v)
+      @shifted = true
+      super
+    end
+
+    def shifted?
+      @shifted
+    end
+  end
+
+  class SubArray2 < Array
+    def to_json(*a)
+      {
+        JSON.create_id => self.class.name,
+        'ary'          => to_a,
+      }.to_json(*a)
+    end
+
+    def self.json_create(o)
+      o.delete JSON.create_id
+      o['ary']
+    end
+  end
+
+  class SubArrayWrapper
+    def initialize
+      @data = []
+    end
+
+    attr_reader :data
+
+    def [](index)
+      @data[index]
+    end
+
+    def <<(value)
+      @data << value
+      @shifted = true
+    end
+
+    def shifted?
+      @shifted
+    end
+  end
+
+  def test_parse_array_custom_array_derived_class
+    res = parse('[1,2]', :array_class => SubArray)
+    assert_equal([1,2], res)
+    assert_equal(SubArray, res.class)
+    assert res.shifted?
+  end
+
+  def test_parse_array_custom_non_array_derived_class
+    res = parse('[1,2]', :array_class => SubArrayWrapper)
+    assert_equal([1,2], res.data)
+    assert_equal(SubArrayWrapper, res.class)
+    assert res.shifted?
+  end
+
+  def test_parse_object
+    assert_equal({}, parse('{}'))
+    assert_equal({}, parse('  {  }  '))
+    assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}'))
+    assert_equal({'foo'=>'bar'}, parse('    { "foo"  :   "bar"   }   '))
+  end
+
+  class SubHash < Hash
+    def []=(k, v)
+      @item_set = true
+      super
+    end
+
+    def item_set?
+      @item_set
+    end
+  end
+
+  class SubHash2 < Hash
+    def to_json(*a)
+      {
+        JSON.create_id => self.class.name,
+      }.merge(self).to_json(*a)
+    end
+
+    def self.json_create(o)
+      o.delete JSON.create_id
+      self[o]
+    end
+  end
+
+  class SubOpenStruct < OpenStruct
+    def [](k)
+      __send__(k)
+    end
+
+    def []=(k, v)
+      @item_set = true
+      __send__("#{k}=", v)
+    end
+
+    def item_set?
+      @item_set
+    end
+  end
+
+  def test_parse_object_custom_hash_derived_class
+    res = parse('{"foo":"bar"}', :object_class => SubHash)
+    assert_equal({"foo" => "bar"}, res)
+    assert_equal(SubHash, res.class)
+    assert res.item_set?
+  end
+
+  def test_parse_object_custom_non_hash_derived_class
+    res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
+    assert_equal "bar", res.foo
+    assert_equal(SubOpenStruct, res.class)
+    assert res.item_set?
+  end
+
+  def test_parse_generic_object
+    res = parse(
+      '{"foo":"bar", "baz":{}}',
+      :object_class => JSON::GenericObject
+    )
+    assert_equal(JSON::GenericObject, res.class)
+    assert_equal "bar", res.foo
+    assert_equal "bar", res["foo"]
+    assert_equal "bar", res[:foo]
+    assert_equal "bar", res.to_hash[:foo]
+    assert_equal(JSON::GenericObject, res.baz.class)
+  end
+
+  def test_generate_core_subclasses_with_new_to_json
+    obj = SubHash2["foo" => SubHash2["bar" => true]]
+    obj_json = JSON(obj)
+    obj_again = parse(obj_json, :create_additions => true)
+    assert_kind_of SubHash2, obj_again
+    assert_kind_of SubHash2, obj_again['foo']
+    assert obj_again['foo']['bar']
+    assert_equal obj, obj_again
+    assert_equal ["foo"],
+      JSON(JSON(SubArray2["foo"]), :create_additions => true)
+  end
+
+  def test_generate_core_subclasses_with_default_to_json
+    assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"])
+    assert_equal '["foo"]', JSON(SubArray["foo"])
+  end
+
+  def test_generate_of_core_subclasses
+    obj = SubHash["foo" => SubHash["bar" => true]]
+    obj_json = JSON(obj)
+    obj_again = JSON(obj_json)
+    assert_kind_of Hash, obj_again
+    assert_kind_of Hash, obj_again['foo']
+    assert obj_again['foo']['bar']
+    assert_equal obj, obj_again
+  end
+
+  def test_parsing_frozen_ascii8bit_string
+    assert_equal(
+      { 'foo' => 'bar' },
+      JSON('{ "foo": "bar" }'.force_encoding(Encoding::ASCII_8BIT).freeze)
+    )
+  end
+
+  private
+
+  def assert_equal_float(expected, actual, delta = 1e-2)
+    Array === expected and expected = expected.first
+    Array === actual and actual = actual.first
+    assert_in_delta(expected, actual, delta)
+  end
+end
diff --git a/tests/json_string_matching_test.rb b/tests/json_string_matching_test.rb
new file mode 100644
index 0000000..5d55dc3
--- /dev/null
+++ b/tests/json_string_matching_test.rb
@@ -0,0 +1,38 @@
+#frozen_string_literal: false
+require 'test_helper'
+require 'time'
+
+class JSONStringMatchingTest < Test::Unit::TestCase
+  include JSON
+
+  class TestTime < ::Time
+    def self.json_create(string)
+      Time.parse(string)
+    end
+
+    def to_json(*)
+      %{"#{strftime('%FT%T%z')}"}
+    end
+
+    def ==(other)
+      to_i == other.to_i
+    end
+  end
+
+  def test_match_date
+    t = TestTime.new
+    t_json = [ t ].to_json
+    time_regexp = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/
+    assert_equal [ t ],
+      parse(
+        t_json,
+        :create_additions => true,
+        :match_string => { time_regexp => TestTime }
+      )
+    assert_equal [ t.strftime('%FT%T%z') ],
+      parse(
+        t_json,
+        :match_string => { time_regexp => TestTime }
+      )
+  end
+end
diff --git a/tests/setup_variant.rb b/tests/test_helper.rb
similarity index 55%
rename from tests/setup_variant.rb
rename to tests/test_helper.rb
index 2dab184..7e99c29 100644
--- a/tests/setup_variant.rb
+++ b/tests/test_helper.rb
@@ -9,3 +9,13 @@ else
   $:.unshift 'ext', 'lib'
   require 'json'
 end
+
+require 'test/unit'
+begin
+  require 'byebug'
+rescue LoadError
+end
+if ENV['START_SIMPLECOV'].to_i == 1
+  require 'simplecov'
+  SimpleCov.start
+end
diff --git a/tests/test_json.rb b/tests/test_json.rb
deleted file mode 100755
index 7957773..0000000
--- a/tests/test_json.rb
+++ /dev/null
@@ -1,553 +0,0 @@
-#!/usr/bin/env ruby
-# encoding: utf-8
-
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
-require 'stringio'
-require 'tempfile'
-require 'ostruct'
-
-unless Array.method_defined?(:permutation)
-  begin
-    require 'enumerator'
-    require 'permutation'
-    class Array
-      def permutation
-        Permutation.for(self).to_enum.map { |x| x.project }
-      end
-    end
-  rescue LoadError
-    warn "Skipping permutation tests."
-  end
-end
-
-class TestJSON < Test::Unit::TestCase
-  include JSON
-
-  def setup
-    @ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true].map do
-      |x| [x]
-    end
-    @ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null",
-      "[1,-2,3]", "false", "true"].map do
-      |x| "[#{x}]"
-    end
-    @hash = {
-      'a' => 2,
-      'b' => 3.141,
-      'c' => 'c',
-      'd' => [ 1, "b", 3.14 ],
-      'e' => { 'foo' => 'bar' },
-      'g' => "\"\0\037",
-      'h' => 1000.0,
-      'i' => 0.001
-    }
-    @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
-      '"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}'
-  end
-
-  def test_construction
-    parser = JSON::Parser.new('test')
-    assert_equal 'test', parser.source
-  end
-
-  def assert_equal_float(expected, is)
-    assert_in_delta(expected.first, is.first, 1e-2)
-  end
-
-  def test_parse_simple_arrays
-    assert_equal([], parse('[]'))
-    assert_equal([], parse('  [  ] '))
-    assert_equal([nil], parse('[null]'))
-    assert_equal([false], parse('[false]'))
-    assert_equal([true], parse('[true]'))
-    assert_equal([-23], parse('[-23]'))
-    assert_equal([23], parse('[23]'))
-    assert_equal([0.23], parse('[0.23]'))
-    assert_equal([0.0], parse('[0e0]'))
-    assert_raises(JSON::ParserError) { parse('[+23.2]') }
-    assert_raises(JSON::ParserError) { parse('[+23]') }
-    assert_raises(JSON::ParserError) { parse('[.23]') }
-    assert_raises(JSON::ParserError) { parse('[023]') }
-    assert_equal_float [3.141], parse('[3.141]')
-    assert_equal_float [-3.141], parse('[-3.141]')
-    assert_equal_float [3.141], parse('[3141e-3]')
-    assert_equal_float [3.141], parse('[3141.1e-3]')
-    assert_equal_float [3.141], parse('[3141E-3]')
-    assert_equal_float [3.141], parse('[3141.0E-3]')
-    assert_equal_float [-3.141], parse('[-3141.0e-3]')
-    assert_equal_float [-3.141], parse('[-3141e-3]')
-    assert_raises(ParserError) { parse('[NaN]') }
-    assert parse('[NaN]', :allow_nan => true).first.nan?
-    assert_raises(ParserError) { parse('[Infinity]') }
-    assert_equal [1.0/0], parse('[Infinity]', :allow_nan => true)
-    assert_raises(ParserError) { parse('[-Infinity]') }
-    assert_equal [-1.0/0], parse('[-Infinity]', :allow_nan => true)
-    assert_equal([""], parse('[""]'))
-    assert_equal(["foobar"], parse('["foobar"]'))
-    assert_equal([{}], parse('[{}]'))
-  end
-
-  def test_parse_simple_objects
-    assert_equal({}, parse('{}'))
-    assert_equal({}, parse(' {   }   '))
-    assert_equal({ "a" => nil }, parse('{   "a"   :  null}'))
-    assert_equal({ "a" => nil }, parse('{"a":null}'))
-    assert_equal({ "a" => false }, parse('{   "a"  :  false  }  '))
-    assert_equal({ "a" => false }, parse('{"a":false}'))
-    assert_raises(JSON::ParserError) { parse('{false}') }
-    assert_equal({ "a" => true }, parse('{"a":true}'))
-    assert_equal({ "a" => true }, parse('  { "a" :  true  }   '))
-    assert_equal({ "a" => -23 }, parse('  {  "a"  :  -23  }  '))
-    assert_equal({ "a" => -23 }, parse('  { "a" : -23 } '))
-    assert_equal({ "a" => 23 }, parse('{"a":23  } '))
-    assert_equal({ "a" => 23 }, parse('  { "a"  : 23  } '))
-    assert_equal({ "a" => 0.23 }, parse(' { "a"  :  0.23 }  '))
-    assert_equal({ "a" => 0.23 }, parse('  {  "a"  :  0.23  }  '))
-  end
-
-  def test_parse_json_primitive_values
-    assert_raise(JSON::ParserError) { JSON.parse('') }
-    assert_raise(JSON::ParserError) { JSON.parse('', :quirks_mode => true) }
-    assert_raise(TypeError) { JSON::Parser.new(nil).parse }
-    assert_raise(TypeError) { JSON::Parser.new(nil, :quirks_mode => true).parse }
-    assert_raise(TypeError) { JSON.parse(nil) }
-    assert_raise(TypeError) { JSON.parse(nil, :quirks_mode => true) }
-    assert_raise(JSON::ParserError) { JSON.parse('  /* foo */ ') }
-    assert_raise(JSON::ParserError) { JSON.parse('  /* foo */ ', :quirks_mode => true) }
-    parser = JSON::Parser.new('null')
-    assert_equal false, parser.quirks_mode?
-    assert_raise(JSON::ParserError) { parser.parse }
-    assert_raise(JSON::ParserError) { JSON.parse('null') }
-    assert_equal nil, JSON.parse('null', :quirks_mode => true)
-    parser = JSON::Parser.new('null', :quirks_mode => true)
-    assert_equal true, parser.quirks_mode?
-    assert_equal nil, parser.parse
-    assert_raise(JSON::ParserError) { JSON.parse('false') }
-    assert_equal false, JSON.parse('false', :quirks_mode => true)
-    assert_raise(JSON::ParserError) { JSON.parse('true') }
-    assert_equal true, JSON.parse('true', :quirks_mode => true)
-    assert_raise(JSON::ParserError) { JSON.parse('23') }
-    assert_equal 23, JSON.parse('23', :quirks_mode => true)
-    assert_raise(JSON::ParserError) { JSON.parse('1') }
-    assert_equal 1, JSON.parse('1', :quirks_mode => true)
-    assert_raise(JSON::ParserError) { JSON.parse('3.141') }
-    assert_in_delta 3.141, JSON.parse('3.141', :quirks_mode => true), 1E-3
-    assert_raise(JSON::ParserError) { JSON.parse('18446744073709551616') }
-    assert_equal 2 ** 64, JSON.parse('18446744073709551616', :quirks_mode => true)
-    assert_raise(JSON::ParserError) { JSON.parse('"foo"') }
-    assert_equal 'foo', JSON.parse('"foo"', :quirks_mode => true)
-    assert_raise(JSON::ParserError) { JSON.parse('NaN', :allow_nan => true) }
-    assert JSON.parse('NaN', :quirks_mode => true, :allow_nan => true).nan?
-    assert_raise(JSON::ParserError) { JSON.parse('Infinity', :allow_nan => true) }
-    assert JSON.parse('Infinity', :quirks_mode => true, :allow_nan => true).infinite?
-    assert_raise(JSON::ParserError) { JSON.parse('-Infinity', :allow_nan => true) }
-    assert JSON.parse('-Infinity', :quirks_mode => true, :allow_nan => true).infinite?
-    assert_raise(JSON::ParserError) { JSON.parse('[ 1, ]', :quirks_mode => true) }
-  end
-
-  if Array.method_defined?(:permutation)
-    def test_parse_more_complex_arrays
-      a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
-      a.permutation.each do |perm|
-        json = pretty_generate(perm)
-        assert_equal perm, parse(json)
-      end
-    end
-
-    def test_parse_complex_objects
-      a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
-      a.permutation.each do |perm|
-        s = "a"
-        orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
-        json = pretty_generate(orig_obj)
-        assert_equal orig_obj, parse(json)
-      end
-    end
-  end
-
-  def test_parse_arrays
-    assert_equal([1,2,3], parse('[1,2,3]'))
-    assert_equal([1.2,2,3], parse('[1.2,2,3]'))
-    assert_equal([[],[[],[]]], parse('[[],[[],[]]]'))
-  end
-
-  def test_parse_values
-    assert_equal([""], parse('[""]'))
-    assert_equal(["\\"], parse('["\\\\"]'))
-    assert_equal(['"'], parse('["\""]'))
-    assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]'))
-    assert_equal(["\"\b\n\r\t\0\037"],
-      parse('["\"\b\n\r\t\u0000\u001f"]'))
-    for i in 0 ... @ary.size
-      assert_equal(@ary[i], parse(@ary_to_parse[i]))
-    end
-  end
-
-  def test_parse_array
-    assert_equal([], parse('[]'))
-    assert_equal([], parse('  [  ]  '))
-    assert_equal([1], parse('[1]'))
-    assert_equal([1], parse('  [ 1  ]  '))
-    assert_equal(@ary,
-      parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]]'\
-      ',[false],[true]]'))
-    assert_equal(@ary, parse(%Q{   [   [1] , ["foo"]  ,  [3.14] \t ,  [47.11e+2]\s
-      , [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ]  }))
-  end
-
-  class SubArray < Array
-    def <<(v)
-      @shifted = true
-      super
-    end
-
-    def shifted?
-      @shifted
-    end
-  end
-
-  class SubArray2 < Array
-    def to_json(*a)
-      {
-        JSON.create_id => self.class.name,
-        'ary'          => to_a,
-      }.to_json(*a)
-    end
-
-    def self.json_create(o)
-      o.delete JSON.create_id
-      o['ary']
-    end
-  end
-
-  class SubArrayWrapper
-    def initialize
-      @data = []
-    end
-
-    attr_reader :data
-
-    def [](index)
-      @data[index]
-    end
-
-    def <<(value)
-      @data << value
-      @shifted = true
-    end
-
-    def shifted?
-      @shifted
-    end
-  end
-
-  def test_parse_array_custom_array_derived_class
-    res = parse('[1,2]', :array_class => SubArray)
-    assert_equal([1,2], res)
-    assert_equal(SubArray, res.class)
-    assert res.shifted?
-  end
-
-  def test_parse_array_custom_non_array_derived_class
-    res = parse('[1,2]', :array_class => SubArrayWrapper)
-    assert_equal([1,2], res.data)
-    assert_equal(SubArrayWrapper, res.class)
-    assert res.shifted?
-  end
-
-  def test_parse_object
-    assert_equal({}, parse('{}'))
-    assert_equal({}, parse('  {  }  '))
-    assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}'))
-    assert_equal({'foo'=>'bar'}, parse('    { "foo"  :   "bar"   }   '))
-  end
-
-  class SubHash < Hash
-    def []=(k, v)
-      @item_set = true
-      super
-    end
-
-    def item_set?
-      @item_set
-    end
-  end
-
-  class SubHash2 < Hash
-    def to_json(*a)
-      {
-        JSON.create_id => self.class.name,
-      }.merge(self).to_json(*a)
-    end
-
-    def self.json_create(o)
-      o.delete JSON.create_id
-      self[o]
-    end
-  end
-
-  class SubOpenStruct < OpenStruct
-    def [](k)
-      __send__(k)
-    end
-
-    def []=(k, v)
-      @item_set = true
-      __send__("#{k}=", v)
-    end
-
-    def item_set?
-      @item_set
-    end
-  end
-
-  def test_parse_object_custom_hash_derived_class
-    res = parse('{"foo":"bar"}', :object_class => SubHash)
-    assert_equal({"foo" => "bar"}, res)
-    assert_equal(SubHash, res.class)
-    assert res.item_set?
-  end
-
-  def test_parse_object_custom_non_hash_derived_class
-    res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
-    assert_equal "bar", res.foo
-    assert_equal(SubOpenStruct, res.class)
-    assert res.item_set?
-  end
-
-  def test_parse_generic_object
-    res = parse('{"foo":"bar", "baz":{}}', :object_class => JSON::GenericObject)
-    assert_equal(JSON::GenericObject, res.class)
-    assert_equal "bar", res.foo
-    assert_equal "bar", res["foo"]
-    assert_equal "bar", res[:foo]
-    assert_equal "bar", res.to_hash[:foo]
-    assert_equal(JSON::GenericObject, res.baz.class)
-  end
-
-  def test_generate_core_subclasses_with_new_to_json
-    obj = SubHash2["foo" => SubHash2["bar" => true]]
-    obj_json = JSON(obj)
-    obj_again = JSON.parse(obj_json, :create_additions => true)
-    assert_kind_of SubHash2, obj_again
-    assert_kind_of SubHash2, obj_again['foo']
-    assert obj_again['foo']['bar']
-    assert_equal obj, obj_again
-    assert_equal ["foo"], JSON(JSON(SubArray2["foo"]), :create_additions => true)
-  end
-
-  def test_generate_core_subclasses_with_default_to_json
-    assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"])
-    assert_equal '["foo"]', JSON(SubArray["foo"])
-  end
-
-  def test_generate_of_core_subclasses
-    obj = SubHash["foo" => SubHash["bar" => true]]
-    obj_json = JSON(obj)
-    obj_again = JSON(obj_json)
-    assert_kind_of Hash, obj_again
-    assert_kind_of Hash, obj_again['foo']
-    assert obj_again['foo']['bar']
-    assert_equal obj, obj_again
-  end
-
-  def test_parser_reset
-    parser = Parser.new(@json)
-    assert_equal(@hash, parser.parse)
-    assert_equal(@hash, parser.parse)
-  end
-
-  def test_comments
-    json = <<EOT
-{
-  "key1":"value1", // eol comment
-  "key2":"value2"  /* multi line
-                    *  comment */,
-  "key3":"value3"  /* multi line
-                    // nested eol comment
-                    *  comment */
-}
-EOT
-    assert_equal(
-      { "key1" => "value1", "key2" => "value2", "key3" => "value3" },
-      parse(json))
-    json = <<EOT
-{
-  "key1":"value1"  /* multi line
-                    // nested eol comment
-                    /* illegal nested multi line comment */
-                    *  comment */
-}
-EOT
-    assert_raises(ParserError) { parse(json) }
-    json = <<EOT
-{
-  "key1":"value1"  /* multi line
-                   // nested eol comment
-                   closed multi comment */
-                   and again, throw an Error */
-}
-EOT
-    assert_raises(ParserError) { parse(json) }
-    json = <<EOT
-{
-  "key1":"value1"  /*/*/
-}
-EOT
-    assert_equal({ "key1" => "value1" }, parse(json))
-  end
-
-  def test_backslash
-    data = [ '\\.(?i:gif|jpe?g|png)$' ]
-    json = '["\\\\.(?i:gif|jpe?g|png)$"]'
-    assert_equal json, JSON.generate(data)
-    assert_equal data, JSON.parse(json)
-    #
-    data = [ '\\"' ]
-    json = '["\\\\\""]'
-    assert_equal json, JSON.generate(data)
-    assert_equal data, JSON.parse(json)
-    #
-    json = '["/"]'
-    data = JSON.parse(json)
-    assert_equal ['/'], data
-    assert_equal json, JSON.generate(data)
-    #
-    json = '["\""]'
-    data = JSON.parse(json)
-    assert_equal ['"'], data
-    assert_equal json, JSON.generate(data)
-    json = '["\\\'"]'
-    data = JSON.parse(json)
-    assert_equal ["'"], data
-    assert_equal '["\'"]', JSON.generate(data)
-  end
-
-  def test_wrong_inputs
-    assert_raises(ParserError) { JSON.parse('"foo"') }
-    assert_raises(ParserError) { JSON.parse('123') }
-    assert_raises(ParserError) { JSON.parse('[] bla') }
-    assert_raises(ParserError) { JSON.parse('[] 1') }
-    assert_raises(ParserError) { JSON.parse('[] []') }
-    assert_raises(ParserError) { JSON.parse('[] {}') }
-    assert_raises(ParserError) { JSON.parse('{} []') }
-    assert_raises(ParserError) { JSON.parse('{} {}') }
-    assert_raises(ParserError) { JSON.parse('[NULL]') }
-    assert_raises(ParserError) { JSON.parse('[FALSE]') }
-    assert_raises(ParserError) { JSON.parse('[TRUE]') }
-    assert_raises(ParserError) { JSON.parse('[07]    ') }
-    assert_raises(ParserError) { JSON.parse('[0a]') }
-    assert_raises(ParserError) { JSON.parse('[1.]') }
-    assert_raises(ParserError) { JSON.parse('     ') }
-  end
-
-  def test_nesting
-    assert_raises(JSON::NestingError) { JSON.parse '[[]]', :max_nesting => 1 }
-    assert_raises(JSON::NestingError) { JSON.parser.new('[[]]', :max_nesting => 1).parse }
-    assert_equal [[]], JSON.parse('[[]]', :max_nesting => 2)
-    too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
-    too_deep_ary = eval too_deep
-    assert_raises(JSON::NestingError) { JSON.parse too_deep }
-    assert_raises(JSON::NestingError) { JSON.parser.new(too_deep).parse }
-    assert_raises(JSON::NestingError) { JSON.parse too_deep, :max_nesting => 100 }
-    ok = JSON.parse too_deep, :max_nesting => 101
-    assert_equal too_deep_ary, ok
-    ok = JSON.parse too_deep, :max_nesting => nil
-    assert_equal too_deep_ary, ok
-    ok = JSON.parse too_deep, :max_nesting => false
-    assert_equal too_deep_ary, ok
-    ok = JSON.parse too_deep, :max_nesting => 0
-    assert_equal too_deep_ary, ok
-    assert_raises(JSON::NestingError) { JSON.generate [[]], :max_nesting => 1 }
-    assert_equal '[[]]', JSON.generate([[]], :max_nesting => 2)
-    assert_raises(JSON::NestingError) { JSON.generate too_deep_ary }
-    assert_raises(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 }
-    ok = JSON.generate too_deep_ary, :max_nesting => 101
-    assert_equal too_deep, ok
-    ok = JSON.generate too_deep_ary, :max_nesting => nil
-    assert_equal too_deep, ok
-    ok = JSON.generate too_deep_ary, :max_nesting => false
-    assert_equal too_deep, ok
-    ok = JSON.generate too_deep_ary, :max_nesting => 0
-    assert_equal too_deep, ok
-  end
-
-  def test_symbolize_names
-    assert_equal({ "foo" => "bar", "baz" => "quux" },
-      JSON.parse('{"foo":"bar", "baz":"quux"}'))
-    assert_equal({ :foo => "bar", :baz => "quux" },
-      JSON.parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true))
-  end
-
-  def test_load
-    assert_equal @hash, JSON.load(@json)
-    tempfile = Tempfile.open('json')
-    tempfile.write @json
-    tempfile.rewind
-    assert_equal @hash, JSON.load(tempfile)
-    stringio = StringIO.new(@json)
-    stringio.rewind
-    assert_equal @hash, JSON.load(stringio)
-    assert_equal nil, JSON.load(nil)
-    assert_equal nil, JSON.load('')
-  ensure
-    tempfile.close!
-  end
-
-  def test_load_with_options
-    small_hash  = JSON("foo" => 'bar')
-    symbol_hash = { :foo => 'bar' }
-    assert_equal symbol_hash, JSON.load(small_hash, nil, :symbolize_names => true)
-  end
-
-  def test_dump
-    too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
-    assert_equal too_deep, JSON.dump(eval(too_deep))
-    assert_kind_of String, Marshal.dump(eval(too_deep))
-    assert_raises(ArgumentError) { JSON.dump(eval(too_deep), 100) }
-    assert_raises(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
-    assert_equal too_deep, JSON.dump(eval(too_deep), 101)
-    assert_kind_of String, Marshal.dump(eval(too_deep), 101)
-    output = StringIO.new
-    JSON.dump(eval(too_deep), output)
-    assert_equal too_deep, output.string
-    output = StringIO.new
-    JSON.dump(eval(too_deep), output, 101)
-    assert_equal too_deep, output.string
-  end
-
-  def test_dump_should_modify_defaults
-    max_nesting = JSON.dump_default_options[:max_nesting]
-    JSON.dump([], StringIO.new, 10)
-    assert_equal max_nesting, JSON.dump_default_options[:max_nesting]
-  end
-
-  def test_big_integers
-    json1 = JSON([orig = (1 << 31) - 1])
-    assert_equal orig, JSON[json1][0]
-    json2 = JSON([orig = 1 << 31])
-    assert_equal orig, JSON[json2][0]
-    json3 = JSON([orig = (1 << 62) - 1])
-    assert_equal orig, JSON[json3][0]
-    json4 = JSON([orig = 1 << 62])
-    assert_equal orig, JSON[json4][0]
-    json5 = JSON([orig = 1 << 64])
-    assert_equal orig, JSON[json5][0]
-  end
-
-  if defined?(JSON::Ext::Parser)
-    def test_allocate
-      parser = JSON::Ext::Parser.new("{}")
-      assert_raise(TypeError, '[ruby-core:35079]') {parser.__send__(:initialize, "{}")}
-      parser = JSON::Ext::Parser.allocate
-      assert_raise(TypeError, '[ruby-core:35079]') {parser.source}
-    end
-  end
-
-  def test_argument_encoding
-    source = "{}".force_encoding("ascii-8bit")
-    JSON::Parser.new(source)
-    assert_equal Encoding::ASCII_8BIT, source.encoding
-  end if defined?(Encoding::ASCII_8BIT)
-end
diff --git a/tests/test_json_encoding.rb b/tests/test_json_encoding.rb
deleted file mode 100644
index fa7d878..0000000
--- a/tests/test_json_encoding.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env ruby
-# encoding: utf-8
-
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
-
-class TestJSONEncoding < Test::Unit::TestCase
-  include JSON
-
-  def setup
-    @utf_8 = '["© ≠ €!"]'
-    @parsed = [ "© ≠ €!" ]
-    @generated = '["\u00a9 \u2260 \u20ac!"]'
-    if String.method_defined?(:encode)
-      @utf_16_data = [@parsed.first.encode('utf-16be', 'utf-8')]
-      @utf_8_ascii_8bit = @utf_8.dup.force_encoding(Encoding::ASCII_8BIT)
-      @utf_16be = @utf_8.encode('utf-16be', 'utf-8')
-      @utf_16be_ascii_8bit = @utf_16be.dup.force_encoding(Encoding::ASCII_8BIT)
-      @utf_16le = @utf_8.encode('utf-16le', 'utf-8')
-      @utf_16le_ascii_8bit = @utf_16le.dup.force_encoding(Encoding::ASCII_8BIT)
-      @utf_32be = @utf_8.encode('utf-32be', 'utf-8')
-      @utf_32be_ascii_8bit = @utf_32be.dup.force_encoding(Encoding::ASCII_8BIT)
-      @utf_32le = @utf_8.encode('utf-32le', 'utf-8')
-      @utf_32le_ascii_8bit = @utf_32le.dup.force_encoding(Encoding::ASCII_8BIT)
-    else
-      require 'iconv'
-      @utf_16_data = Iconv.iconv('utf-16be', 'utf-8', @parsed.first)
-      @utf_8_ascii_8bit = @utf_8.dup
-      @utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8)
-      @utf_16be_ascii_8bit = @utf_16be.dup
-      @utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8)
-      @utf_16le_ascii_8bit = @utf_16le.dup
-      @utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8)
-      @utf_32be_ascii_8bit = @utf_32be.dup
-      @utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8)
-      @utf_32le_ascii_8bit = @utf_32le.dup
-    end
-  end
-
-  def test_parse
-    assert_equal @parsed, JSON.parse(@utf_8)
-    assert_equal @parsed, JSON.parse(@utf_16be)
-    assert_equal @parsed, JSON.parse(@utf_16le)
-    assert_equal @parsed, JSON.parse(@utf_32be)
-    assert_equal @parsed, JSON.parse(@utf_32le)
-  end
-
-  def test_parse_ascii_8bit
-    assert_equal @parsed, JSON.parse(@utf_8_ascii_8bit)
-    assert_equal @parsed, JSON.parse(@utf_16be_ascii_8bit)
-    assert_equal @parsed, JSON.parse(@utf_16le_ascii_8bit)
-    assert_equal @parsed, JSON.parse(@utf_32be_ascii_8bit)
-    assert_equal @parsed, JSON.parse(@utf_32le_ascii_8bit)
-  end
-
-  def test_generate
-    assert_equal @generated, JSON.generate(@parsed, :ascii_only => true)
-    if defined?(::Encoding)
-      assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true)
-    else
-      # XXX checking of correct utf8 data is not as strict (yet?) without :ascii_only
-      assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data, :ascii_only => true) }
-    end
-  end
-end
diff --git a/tests/test_json_string_matching.rb b/tests/test_json_string_matching.rb
deleted file mode 100644
index c233df8..0000000
--- a/tests/test_json_string_matching.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env ruby
-# encoding: utf-8
-
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
-require 'stringio'
-require 'time'
-
-class TestJSONStringMatching < Test::Unit::TestCase
-  include JSON
-
-  class TestTime < ::Time
-    def self.json_create(string)
-      Time.parse(string)
-    end
-
-    def to_json(*)
-      %{"#{strftime('%FT%T%z')}"}
-    end
-
-    def ==(other)
-      to_i == other.to_i
-    end
-  end
-
-  def test_match_date
-    t = TestTime.new
-    t_json = [ t ].to_json
-    assert_equal [ t ],
-      JSON.parse(t_json, :create_additions => true,
-        :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
-    assert_equal [ t.strftime('%FT%T%z') ],
-      JSON.parse(t_json, :create_additions => true,
-        :match_string => { /\A\d{3}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
-    assert_equal [ t.strftime('%FT%T%z') ],
-      JSON.parse(t_json,
-        :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
-  end
-end
diff --git a/tests/test_json_unicode.rb b/tests/test_json_unicode.rb
deleted file mode 100755
index 8352d5c..0000000
--- a/tests/test_json_unicode.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env ruby
-# encoding: utf-8
-
-require 'test/unit'
-require File.join(File.dirname(__FILE__), 'setup_variant')
-
-class TestJSONUnicode < Test::Unit::TestCase
-  include JSON
-
-  def test_unicode
-    assert_equal '""', ''.to_json
-    assert_equal '"\\b"', "\b".to_json
-    assert_equal '"\u0001"', 0x1.chr.to_json
-    assert_equal '"\u001f"', 0x1f.chr.to_json
-    assert_equal '" "', ' '.to_json
-    assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
-    utf8 = [ "© ≠ €! \01" ]
-    json = '["© ≠ €! \u0001"]'
-    assert_equal json, utf8.to_json(:ascii_only => false)
-    assert_equal utf8, parse(json)
-    json = '["\u00a9 \u2260 \u20ac! \u0001"]'
-    assert_equal json, utf8.to_json(:ascii_only => true)
-    assert_equal utf8, parse(json)
-    utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
-    json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]"
-    assert_equal utf8, parse(json)
-    assert_equal json, utf8.to_json(:ascii_only => false)
-    utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
-    assert_equal utf8, parse(json)
-    json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
-    assert_equal json, utf8.to_json(:ascii_only => true)
-    assert_equal utf8, parse(json)
-    utf8 = ['საქართველო']
-    json = '["საქართველო"]'
-    assert_equal json, utf8.to_json(:ascii_only => false)
-    json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
-    assert_equal json, utf8.to_json(:ascii_only => true)
-    assert_equal utf8, parse(json)
-    assert_equal '["Ã"]', JSON.generate(["Ã"], :ascii_only => false)
-    assert_equal '["\\u00c3"]', JSON.generate(["Ã"], :ascii_only => true)
-    assert_equal ["€"], JSON.parse('["\u20ac"]')
-    utf8 = ["\xf0\xa0\x80\x81"]
-    json = "[\"\xf0\xa0\x80\x81\"]"
-    assert_equal json, JSON.generate(utf8, :ascii_only => false)
-    assert_equal utf8, JSON.parse(json)
-    json = '["\ud840\udc01"]'
-    assert_equal json, JSON.generate(utf8, :ascii_only => true)
-    assert_equal utf8, JSON.parse(json)
-  end
-
-  def test_chars
-    (0..0x7f).each do |i|
-      json = '["\u%04x"]' % i
-      if RUBY_VERSION >= "1.9."
-        i = i.chr
-      end
-      assert_equal i, JSON.parse(json).first[0]
-      if i == ?\b
-        generated = JSON.generate(["" << i])
-        assert '["\b"]' == generated || '["\10"]' == generated
-      elsif [?\n, ?\r, ?\t, ?\f].include?(i)
-        assert_equal '[' << ('' << i).dump << ']', JSON.generate(["" << i])
-      elsif i.chr < 0x20.chr
-        assert_equal json, JSON.generate(["" << i])
-      end
-    end
-    assert_raise(JSON::GeneratorError) do
-      JSON.generate(["\x80"], :ascii_only => true)
-    end
-    assert_equal "\302\200", JSON.parse('["\u0080"]').first
-  end
-end
diff --git a/tools/diff.sh b/tools/diff.sh
new file mode 100755
index 0000000..89385b0
--- /dev/null
+++ b/tools/diff.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+files=`find ext -name '*.[ch]' -o -name parser.rl`
+
+for f in $files
+do
+  b=`basename $f`
+  g=`find ../ruby/ext/json -name $b`
+  d=`diff -u $f $g`
+  test -z "$d" && continue
+  echo "$d"
+  read -p "Edit diff of $b? " a
+  case $a in
+  [yY]*)
+    vimdiff $f $g
+    ;;
+  esac
+done
diff --git a/tools/fuzz.rb b/tools/fuzz.rb
index c0fae12..8261930 100755
--- a/tools/fuzz.rb
+++ b/tools/fuzz.rb
@@ -1,13 +1,5 @@
 require 'json'
 
-require 'iconv'
-ISO_8859_1_TO_UTF8 = Iconv.new('utf-8', 'iso-8859-15')
-class ::String
-  def to_utf8
-    ISO_8859_1_TO_UTF8.iconv self
-  end
-end
-
 class Fuzzer
   def initialize(n, freqs = {})
     sum = freqs.inject(0.0) { |s, x| s + x.last }
@@ -25,7 +17,7 @@ class Fuzzer
   def random_string
     s = ''
     30.times { s << @alpha[rand(@alpha.size)] }
-    s.to_utf8
+    s
   end
 
   def pick

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-json.git



More information about the Pkg-ruby-extras-commits mailing list