summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README20
-rw-r--r--Rakefile53
-rwxr-xr-xbenchmarks/generator2_benchmark.rb216
-rwxr-xr-xbenchmarks/generator_benchmark.rb61
-rw-r--r--benchmarks/ohai.json1216
-rw-r--r--benchmarks/ohai.ruby1
-rwxr-xr-xbenchmarks/parser2_benchmark.rb237
-rwxr-xr-xbenchmarks/parser_benchmark.rb54
-rw-r--r--ext/json/ext/extconf_generator.rb18
-rw-r--r--ext/json/ext/extconf_parser.rb (renamed from ext/json/ext/parser/extconf.rb)7
-rw-r--r--ext/json/ext/generator.c1256
-rw-r--r--ext/json/ext/generator.h178
-rw-r--r--ext/json/ext/generator/extconf.rb11
-rw-r--r--ext/json/ext/generator/generator.c935
-rw-r--r--ext/json/ext/generator/unicode.c180
-rw-r--r--ext/json/ext/generator/unicode.h53
-rw-r--r--ext/json/ext/parser.c (renamed from ext/json/ext/parser/parser.c)392
-rw-r--r--ext/json/ext/parser.h79
-rw-r--r--ext/json/ext/parser.rl (renamed from ext/json/ext/parser/parser.rl)244
-rw-r--r--ext/json/ext/parser/unicode.c154
-rw-r--r--ext/json/ext/parser/unicode.h58
-rw-r--r--lib/json/common.rb53
-rw-r--r--lib/json/pure/generator.rb158
-rw-r--r--tests/test_json_encoding.rb7
-rwxr-xr-xtests/test_json_generate.rb6
-rwxr-xr-xtests/test_json_unicode.rb26
26 files changed, 3837 insertions, 1836 deletions
diff --git a/README b/README
index 2dd55e2..a12009f 100644
--- a/README
+++ b/README
@@ -1,6 +1,4 @@
-== json - JSON Implementation for Ruby
-
-=== 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
@@ -34,7 +32,7 @@ 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
@@ -65,7 +63,7 @@ with:
# gem install json_pure
-=== Compiling the extensions yourself
+== Compiling the extensions yourself
If you want to build the extensions yourself you need rake:
@@ -82,7 +80,7 @@ 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
-=== Usage
+== Usage
To use JSON you can
require 'json'
@@ -136,7 +134,7 @@ To get the best compatibility to rails' JSON implementation, you can
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:
@@ -226,7 +224,7 @@ 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
@@ -339,17 +337,17 @@ Here are the median comparisons for completeness' sake:
calls/sec ( time) -> speed covers
secs/call
-=== Author
+== Author
Florian Frank <mailto:flori@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.
-=== Download
+== Download
The latest version of this library can be downloaded at
diff --git a/Rakefile b/Rakefile
index b1ff0ce..97273a5 100644
--- a/Rakefile
+++ b/Rakefile
@@ -9,30 +9,29 @@ rescue LoadError
puts "WARNING: rake-compiler is not installed. You will not be able to build the json gem until you install it."
end
-require 'rake/clean'
-CLOBBER.include Dir['benchmarks/data/*.{dat,log}']
-
require 'rbconfig'
include Config
+require 'rake/clean'
+CLOBBER.include Dir['benchmarks/data/*.{dat,log}']
+CLEAN.include FileList['diagrams/*.*'], 'doc', 'coverage', 'tmp',
+ FileList["ext/**/{Makefile,mkmf.log}"],
+ FileList["{ext,lib}/**/*.{so,bundle,#{CONFIG['DLEXT']},o,obj,pdb,lib,manifest,exp,def}"]
+
MAKE = ENV['MAKE'] || %w[gmake make].find { |c| system(c, '-v') }
PKG_NAME = 'json'
+PKG_TITLE = 'JSON Implementation for Ruby'
PKG_VERSION = File.read('VERSION').chomp
PKG_FILES = FileList["**/*"].exclude(/CVS|pkg|tmp|coverage|Makefile|\.nfs\./).exclude(/\.(so|bundle|o|#{CONFIG['DLEXT']})$/)
EXT_ROOT_DIR = 'ext/json/ext'
-EXT_PARSER_DIR = "#{EXT_ROOT_DIR}/parser"
EXT_PARSER_DL = "#{EXT_ROOT_DIR}/parser.#{CONFIG['DLEXT']}"
-EXT_PARSER_SRC = "#{EXT_PARSER_DIR}/parser.c"
+EXT_PARSER_SRC = "#{EXT_ROOT_DIR}/parser.c"
PKG_FILES << EXT_PARSER_SRC
-EXT_GENERATOR_DIR = "#{EXT_ROOT_DIR}/generator"
EXT_GENERATOR_DL = "#{EXT_ROOT_DIR}/generator.#{CONFIG['DLEXT']}"
-EXT_GENERATOR_SRC = "#{EXT_GENERATOR_DIR}/generator.c"
+EXT_GENERATOR_SRC = "#{EXT_ROOT_DIR}/generator.c"
RAGEL_CODEGEN = %w[rlcodegen rlgen-cd ragel].find { |c| system(c, '-v') }
RAGEL_DOTGEN = %w[rlgen-dot rlgen-cd ragel].find { |c| system(c, '-v') }
-RAGEL_PATH = "#{EXT_PARSER_DIR}/parser.rl"
-CLEAN.include FileList['diagrams/*.*'], 'doc', 'coverage', 'tmp',
- FileList["ext/**/{Makefile,mkmf.log}"],
- FileList["{ext,lib}/**/*.{so,bundle,#{CONFIG['DLEXT']},o,obj,pdb,lib,manifest,exp,def}"]
+RAGEL_PATH = "#{EXT_ROOT_DIR}/parser.rl"
def myruby(*args, &block)
@myruby ||= File.join(CONFIG['bindir'], CONFIG['ruby_install_name'])
@@ -74,19 +73,17 @@ desc "Compiling extension"
task :compile_ext => [ EXT_PARSER_DL, EXT_GENERATOR_DL ]
file EXT_PARSER_DL => EXT_PARSER_SRC do
- cd EXT_PARSER_DIR do
- myruby 'extconf.rb'
+ cd EXT_ROOT_DIR do
+ myruby 'extconf_parser.rb'
sh MAKE
end
- cp "#{EXT_PARSER_DIR}/parser.#{CONFIG['DLEXT']}", EXT_ROOT_DIR
end
file EXT_GENERATOR_DL => EXT_GENERATOR_SRC do
- cd EXT_GENERATOR_DIR do
- myruby 'extconf.rb'
+ cd EXT_ROOT_DIR do
+ myruby 'extconf_generator.rb'
sh MAKE
end
- cp "#{EXT_GENERATOR_DIR}/generator.#{CONFIG['DLEXT']}", EXT_ROOT_DIR
end
desc "Generate parser with ragel"
@@ -97,7 +94,7 @@ task :ragel_clean do
end
file EXT_PARSER_SRC => RAGEL_PATH do
- cd EXT_PARSER_DIR do
+ cd EXT_ROOT_DIR do
if RAGEL_CODEGEN == 'ragel'
sh "ragel parser.rl -G2 -o parser.c"
else
@@ -158,12 +155,14 @@ desc "Benchmarking parser"
task :benchmark_parser do
ENV['RUBYOPT'] = "-Ilib:ext #{ENV['RUBYOPT']}"
myruby 'benchmarks/parser_benchmark.rb'
+ myruby 'benchmarks/parser2_benchmark.rb'
end
desc "Benchmarking generator"
task :benchmark_generator do
ENV['RUBYOPT'] = "-Ilib:ext #{ENV['RUBYOPT']}"
myruby 'benchmarks/generator_benchmark.rb'
+ myruby 'benchmarks/generator2_benchmark.rb'
end
desc "Benchmarking library"
@@ -171,14 +170,14 @@ task :benchmark => [ :benchmark_parser, :benchmark_generator ]
desc "Create RDOC documentation"
task :doc => [ :version, EXT_PARSER_SRC ] do
- sh "rdoc -o doc -m README README lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}"
+ sh "rdoc -o doc -t '#{PKG_TITLE}' -m README README lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}"
end
if defined?(Gem) and defined?(Rake::GemPackageTask) and defined?(Rake::ExtensionTask)
spec_pure = Gem::Specification.new do |s|
s.name = 'json_pure'
s.version = PKG_VERSION
- s.summary = "A JSON implementation in Ruby"
+ s.summary = PKG_TITLE
s.description = "This is a JSON implementation in pure Ruby."
s.files = PKG_FILES
@@ -192,7 +191,7 @@ if defined?(Gem) and defined?(Rake::GemPackageTask) and defined?(Rake::Extension
s.has_rdoc = true
s.extra_rdoc_files << 'README'
s.rdoc_options <<
- '--title' << 'JSON -- A JSON implemention' << '--main' << 'README'
+ '--title' << 'JSON implemention for ruby' << '--main' << 'README'
s.test_files.concat Dir['tests/*.rb']
s.author = "Florian Frank"
@@ -209,12 +208,12 @@ if defined?(Gem) and defined?(Rake::GemPackageTask) and defined?(Rake::Extension
spec_ext = Gem::Specification.new do |s|
s.name = 'json'
s.version = PKG_VERSION
- s.summary = "A JSON implementation as a Ruby extension"
+ s.summary = PKG_TITLE
s.description = "This is a JSON implementation as a Ruby extension in C."
s.files = PKG_FILES
- s.extensions = FileList['ext/**/extconf.rb']
+ s.extensions = FileList['ext/**/extconf_*.rb']
s.require_path = EXT_ROOT_DIR
s.require_paths << 'ext'
@@ -227,7 +226,7 @@ if defined?(Gem) and defined?(Rake::GemPackageTask) and defined?(Rake::Extension
s.has_rdoc = true
s.extra_rdoc_files << 'README'
s.rdoc_options <<
- '--title' << 'JSON -- A JSON implemention' << '--main' << 'README'
+ '--title' << 'JSON implemention for Ruby' << '--main' << 'README'
s.test_files.concat Dir['tests/*.rb']
s.author = "Florian Frank"
@@ -243,19 +242,21 @@ if defined?(Gem) and defined?(Rake::GemPackageTask) and defined?(Rake::Extension
Rake::ExtensionTask.new do |ext|
ext.name = 'parser'
+ ext.config_script = 'extconf_parser.rb'
ext.gem_spec = spec_ext
ext.cross_compile = true
ext.cross_platform = %w[i386-mswin32 and i386-mingw32]
- ext.ext_dir = 'ext/json/ext/parser'
+ ext.ext_dir = 'ext/json/ext'
ext.lib_dir = 'lib/json/ext'
end
Rake::ExtensionTask.new do |ext|
ext.name = 'generator'
+ ext.config_script = 'extconf_generator.rb'
ext.gem_spec = spec_ext
ext.cross_compile = true
ext.cross_platform = %w[i386-mswin32 and i386-mingw32]
- ext.ext_dir = 'ext/json/ext/generator'
+ ext.ext_dir = 'ext/json/ext'
ext.lib_dir = 'lib/json/ext'
end
end
diff --git a/benchmarks/generator2_benchmark.rb b/benchmarks/generator2_benchmark.rb
new file mode 100755
index 0000000..7b9fa74
--- /dev/null
+++ b/benchmarks/generator2_benchmark.rb
@@ -0,0 +1,216 @@
+#!/usr/bin/env ruby
+# CODING: UTF-8
+
+require 'rbconfig'
+RUBY_PATH=File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+RAKE_PATH=File.join(Config::CONFIG['bindir'], 'rake')
+require 'bullshit'
+case ARGV.first
+when 'ext'
+ require 'json/ext'
+when 'pure'
+ require 'json/pure'
+when 'rails'
+ require 'active_support'
+when 'yajl'
+ require 'yajl'
+ require 'stringio'
+end
+
+module JSON
+ def self.[](*) end
+end
+
+module Generator2BenchmarkCommon
+ include JSON
+
+ def setup
+ @big = eval File.read(File.join(File.dirname(__FILE__), 'ohai.ruby'))
+ end
+
+ def generic_reset_method
+ @result and @result.size >= 16 or raise @result.to_s
+ end
+end
+
+module JSONGeneratorCommon
+ include Generator2BenchmarkCommon
+
+ def benchmark_generator_fast
+ @result = JSON.fast_generate(@big)
+ end
+
+ alias reset_benchmark_generator_fast generic_reset_method
+
+ def benchmark_generator_safe
+ @result = JSON.generate(@big)
+ end
+
+ alias reset_benchmark_generator_safe generic_reset_method
+
+ def benchmark_generator_pretty
+ @result = JSON.pretty_generate(@big)
+ end
+
+ alias reset_benchmark_generator_pretty generic_reset_method
+
+ def benchmark_generator_ascii
+ @result = JSON.generate(@big, :ascii_only => true)
+ end
+
+ alias reset_benchmark_generator_ascii generic_reset_method
+end
+
+class Generator2BenchmarkExt < Bullshit::RepeatCase
+ include JSONGeneratorCommon
+
+ warmup yes
+ iterations 8000
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+end
+
+class Generator2BenchmarkPure < Bullshit::RepeatCase
+ include JSONGeneratorCommon
+
+ warmup yes
+ iterations 500
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+end
+
+class Generator2BenchmarkRails < Bullshit::RepeatCase
+ include Generator2BenchmarkCommon
+
+ warmup yes
+ iterations 500
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def benchmark_generator
+ @result = @big.to_json
+ end
+
+ alias reset_benchmark_generator generic_reset_method
+end
+
+class Generator2BenchmarkYajl < Bullshit::RepeatCase
+ include Generator2BenchmarkCommon
+
+ warmup yes
+ iterations 8000
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def benchmark_generator
+ output = StringIO.new
+ Yajl::Encoder.new.encode(@big, output)
+ @result = output.string
+ end
+
+ def reset_benchmark_generator
+ generic_reset_method
+ end
+end
+
+if $0 == __FILE__
+ Bullshit::Case.autorun false
+
+ case ARGV.first
+ when 'ext'
+ Generator2BenchmarkExt.run
+ when 'pure'
+ Generator2BenchmarkPure.run
+ when 'rails'
+ Generator2BenchmarkRails.run
+ when 'yajl'
+ Generator2BenchmarkYajl.run
+ else
+ system "#{RAKE_PATH} clean"
+ system "#{RUBY_PATH} #$0 rails"
+ system "#{RUBY_PATH} #$0 pure"
+ system "#{RAKE_PATH} compile_ext"
+ system "#{RUBY_PATH} #$0 ext"
+ system "#{RUBY_PATH} #$0 yajl"
+ Bullshit.compare do
+ output_filename File.join(File.dirname(__FILE__), 'data', 'Generator2BenchmarkComparison.log')
+
+ benchmark Generator2BenchmarkExt, :generator_fast, :load => yes
+ benchmark Generator2BenchmarkExt, :generator_safe, :load => yes
+ benchmark Generator2BenchmarkExt, :generator_pretty, :load => yes
+ benchmark Generator2BenchmarkExt, :generator_ascii, :load => yes
+ benchmark Generator2BenchmarkPure, :generator_fast, :load => yes
+ benchmark Generator2BenchmarkPure, :generator_safe, :load => yes
+ benchmark Generator2BenchmarkPure, :generator_pretty, :load => yes
+ benchmark Generator2BenchmarkPure, :generator_ascii, :load => yes
+ benchmark Generator2BenchmarkRails, :generator, :load => yes
+ benchmark Generator2BenchmarkYajl, :generator, :load => yes
+ end
+ end
+end
+
diff --git a/benchmarks/generator_benchmark.rb b/benchmarks/generator_benchmark.rb
index 1d29e63..9d6aec3 100755
--- a/benchmarks/generator_benchmark.rb
+++ b/benchmarks/generator_benchmark.rb
@@ -12,6 +12,9 @@ when 'pure'
require 'json/pure'
when 'rails'
require 'active_support'
+when 'yajl'
+ require 'yajl'
+ require 'stringio'
end
module JSON
@@ -23,7 +26,7 @@ module GeneratorBenchmarkCommon
def setup
a = [ nil, false, true, "fÖßÄr", [ "n€st€d", true ], { "fooß" => "bär", "quux" => true } ]
- puts a.to_json
+ puts a.to_json if a.respond_to?(:to_json)
@big = a * 100
end
@@ -52,15 +55,22 @@ module JSONGeneratorCommon
end
alias reset_benchmark_generator_pretty generic_reset_method
+
+ def benchmark_generator_ascii
+ @result = JSON.generate(@big, :ascii_only => true)
+ end
+
+ alias reset_benchmark_generator_ascii generic_reset_method
end
class GeneratorBenchmarkExt < Bullshit::RepeatCase
include JSONGeneratorCommon
warmup yes
- iterations 1000
+ iterations 8000
truncate_data do
+ enabled false
alpha_level 0.05
window_size 50
slope_angle 0.1
@@ -83,9 +93,10 @@ class GeneratorBenchmarkPure < Bullshit::RepeatCase
include JSONGeneratorCommon
warmup yes
- iterations 1000
+ iterations 500
truncate_data do
+ enabled false
alpha_level 0.05
window_size 50
slope_angle 0.1
@@ -107,9 +118,10 @@ class GeneratorBenchmarkRails < Bullshit::RepeatCase
include GeneratorBenchmarkCommon
warmup yes
- iterations 1000
+ iterations 500
truncate_data do
+ enabled false
alpha_level 0.05
window_size 50
slope_angle 0.1
@@ -133,6 +145,41 @@ class GeneratorBenchmarkRails < Bullshit::RepeatCase
alias reset_benchmark_generator generic_reset_method
end
+class GeneratorBenchmarkYajl < Bullshit::RepeatCase
+ include GeneratorBenchmarkCommon
+
+ warmup yes
+ iterations 8000
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def benchmark_generator
+ output = StringIO.new
+ Yajl::Encoder.new.encode(@big, output)
+ @result = output.string
+ end
+
+ def reset_benchmark_generator
+ generic_reset_method
+ end
+end
+
if $0 == __FILE__
Bullshit::Case.autorun false
@@ -143,22 +190,28 @@ if $0 == __FILE__
GeneratorBenchmarkPure.run
when 'rails'
GeneratorBenchmarkRails.run
+ when 'yajl'
+ GeneratorBenchmarkYajl.run
else
system "#{RAKE_PATH} clean"
system "#{RUBY_PATH} #$0 rails"
system "#{RUBY_PATH} #$0 pure"
system "#{RAKE_PATH} compile_ext"
system "#{RUBY_PATH} #$0 ext"
+ system "#{RUBY_PATH} #$0 yajl"
Bullshit.compare do
output_filename File.join(File.dirname(__FILE__), 'data', 'GeneratorBenchmarkComparison.log')
benchmark GeneratorBenchmarkExt, :generator_fast, :load => yes
benchmark GeneratorBenchmarkExt, :generator_safe, :load => yes
benchmark GeneratorBenchmarkExt, :generator_pretty, :load => yes
+ benchmark GeneratorBenchmarkExt, :generator_ascii, :load => yes
benchmark GeneratorBenchmarkPure, :generator_fast, :load => yes
benchmark GeneratorBenchmarkPure, :generator_safe, :load => yes
benchmark GeneratorBenchmarkPure, :generator_pretty, :load => yes
+ benchmark GeneratorBenchmarkPure, :generator_ascii, :load => yes
benchmark GeneratorBenchmarkRails, :generator, :load => yes
+ benchmark GeneratorBenchmarkYajl, :generator, :load => yes
end
end
end
diff --git a/benchmarks/ohai.json b/benchmarks/ohai.json
new file mode 100644
index 0000000..584bdbd
--- /dev/null
+++ b/benchmarks/ohai.json
@@ -0,0 +1,1216 @@
+{
+ "command": {
+ "ps": "ps -ef"
+ },
+ "kernel": {
+ "modules": {
+ "org.virtualbox.kext.VBoxDrv": {
+ "size": 118784,
+ "version": "2.2.0",
+ "index": "114",
+ "refcount": "3"
+ },
+ "com.cisco.nke.ipsec": {
+ "size": 454656,
+ "version": "2.0.1",
+ "index": "111",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleAPIC": {
+ "size": 12288,
+ "version": "1.4",
+ "index": "26",
+ "refcount": "0"
+ },
+ "com.apple.driver.AirPort.Atheros": {
+ "size": 593920,
+ "version": "318.8.3",
+ "index": "88",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleIntelCPUPowerManagement": {
+ "size": 102400,
+ "version": "59.0.1",
+ "index": "22",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOStorageFamily": {
+ "size": 98304,
+ "version": "1.5.5",
+ "index": "44",
+ "refcount": "9"
+ },
+ "com.apple.iokit.IOATAPIProtocolTransport": {
+ "size": 16384,
+ "version": "1.5.2",
+ "index": "52",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOPCIFamily": {
+ "size": 65536,
+ "version": "2.5",
+ "index": "17",
+ "refcount": "18"
+ },
+ "com.apple.driver.AppleHPET": {
+ "size": 12288,
+ "version": "1.3",
+ "index": "33",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleUSBHub": {
+ "size": 49152,
+ "version": "3.2.7",
+ "index": "47",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOFireWireFamily": {
+ "size": 258048,
+ "version": "3.4.6",
+ "index": "49",
+ "refcount": "2"
+ },
+ "com.apple.driver.AppleUSBComposite": {
+ "size": 16384,
+ "version": "3.2.0",
+ "index": "60",
+ "refcount": "1"
+ },
+ "com.apple.driver.AppleIntelPIIXATA": {
+ "size": 36864,
+ "version": "2.0.0",
+ "index": "41",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleSmartBatteryManager": {
+ "size": 28672,
+ "version": "158.6.0",
+ "index": "32",
+ "refcount": "0"
+ },
+ "com.apple.filesystems.udf": {
+ "size": 233472,
+ "version": "2.0.2",
+ "index": "119",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOSMBusFamily": {
+ "size": 12288,
+ "version": "1.1",
+ "index": "27",
+ "refcount": "2"
+ },
+ "com.apple.iokit.IOACPIFamily": {
+ "size": 16384,
+ "version": "1.2.0",
+ "index": "18",
+ "refcount": "10"
+ },
+ "foo.tap": {
+ "size": 24576,
+ "version": "1.0",
+ "index": "113",
+ "refcount": "0"
+ },
+ "com.vmware.kext.vmx86": {
+ "size": 864256,
+ "version": "2.0.4",
+ "index": "104",
+ "refcount": "0"
+ },
+ "com.apple.iokit.CHUDUtils": {
+ "size": 28672,
+ "version": "200",
+ "index": "98",
+ "refcount": "0"
+ },
+ "org.virtualbox.kext.VBoxNetAdp": {
+ "size": 8192,
+ "version": "2.2.0",
+ "index": "117",
+ "refcount": "0"
+ },
+ "com.apple.filesystems.autofs": {
+ "size": 45056,
+ "version": "2.0.1",
+ "index": "109",
+ "refcount": "0"
+ },
+ "com.vmware.kext.vmnet": {
+ "size": 36864,
+ "version": "2.0.4",
+ "index": "108",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleACPIButtons": {
+ "size": 16384,
+ "version": "1.2.4",
+ "index": "30",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleFWOHCI": {
+ "size": 139264,
+ "version": "3.7.2",
+ "index": "50",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOSCSIArchitectureModelFamily": {
+ "size": 102400,
+ "version": "2.0.5",
+ "index": "51",
+ "refcount": "4"
+ },
+ "com.apple.iokit.IOSCSIBlockCommandsDevice": {
+ "size": 90112,
+ "version": "2.0.5",
+ "index": "57",
+ "refcount": "1"
+ },
+ "com.apple.driver.AppleACPIPCI": {
+ "size": 12288,
+ "version": "1.2.4",
+ "index": "31",
+ "refcount": "0"
+ },
+ "com.apple.security.seatbelt": {
+ "size": 98304,
+ "version": "107.10",
+ "index": "25",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleUpstreamUserClient": {
+ "size": 16384,
+ "version": "2.7.2",
+ "index": "100",
+ "refcount": "0"
+ },
+ "com.apple.kext.OSvKernDSPLib": {
+ "size": 12288,
+ "version": "1.1",
+ "index": "79",
+ "refcount": "1"
+ },
+ "com.apple.iokit.IOBDStorageFamily": {
+ "size": 20480,
+ "version": "1.5",
+ "index": "58",
+ "refcount": "1"
+ },
+ "com.apple.iokit.IOGraphicsFamily": {
+ "size": 118784,
+ "version": "1.7.1",
+ "index": "70",
+ "refcount": "5"
+ },
+ "com.apple.iokit.IONetworkingFamily": {
+ "size": 90112,
+ "version": "1.6.1",
+ "index": "82",
+ "refcount": "4"
+ },
+ "com.apple.iokit.IOATAFamily": {
+ "size": 53248,
+ "version": "2.0.0",
+ "index": "40",
+ "refcount": "2"
+ },
+ "com.apple.iokit.IOUSBHIDDriver": {
+ "size": 20480,
+ "version": "3.2.2",
+ "index": "63",
+ "refcount": "2"
+ },
+ "org.virtualbox.kext.VBoxUSB": {
+ "size": 28672,
+ "version": "2.2.0",
+ "index": "115",
+ "refcount": "0"
+ },
+ "com.vmware.kext.vmioplug": {
+ "size": 24576,
+ "version": "2.0.4",
+ "index": "107",
+ "refcount": "0"
+ },
+ "com.apple.security.TMSafetyNet": {
+ "size": 12288,
+ "version": "3",
+ "index": "23",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IONDRVSupport": {
+ "size": 57344,
+ "version": "1.7.1",
+ "index": "71",
+ "refcount": "3"
+ },
+ "com.apple.BootCache": {
+ "size": 20480,
+ "version": "30.3",
+ "index": "20",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOUSBUserClient": {
+ "size": 8192,
+ "version": "3.2.4",
+ "index": "46",
+ "refcount": "1"
+ },
+ "com.apple.iokit.IOSCSIMultimediaCommandsDevice": {
+ "size": 90112,
+ "version": "2.0.5",
+ "index": "59",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleIRController": {
+ "size": 20480,
+ "version": "110",
+ "index": "78",
+ "refcount": "0"
+ },
+ "com.apple.driver.AudioIPCDriver": {
+ "size": 16384,
+ "version": "1.0.5",
+ "index": "81",
+ "refcount": "0"
+ },
+ "org.virtualbox.kext.VBoxNetFlt": {
+ "size": 16384,
+ "version": "2.2.0",
+ "index": "116",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleLPC": {
+ "size": 12288,
+ "version": "1.2.11",
+ "index": "73",
+ "refcount": "0"
+ },
+ "com.apple.iokit.CHUDKernLib": {
+ "size": 20480,
+ "version": "196",
+ "index": "93",
+ "refcount": "2"
+ },
+ "com.apple.iokit.CHUDProf": {
+ "size": 49152,
+ "version": "207",
+ "index": "97",
+ "refcount": "0"
+ },
+ "com.apple.NVDAResman": {
+ "size": 2478080,
+ "version": "5.3.6",
+ "index": "90",
+ "refcount": "2"
+ },
+ "com.apple.driver.AppleACPIEC": {
+ "size": 20480,
+ "version": "1.2.4",
+ "index": "28",
+ "refcount": "0"
+ },
+ "foo.tun": {
+ "size": 24576,
+ "version": "1.0",
+ "index": "118",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOSerialFamily": {
+ "size": 36864,
+ "version": "9.3",
+ "index": "102",
+ "refcount": "1"
+ },
+ "com.apple.GeForce": {
+ "size": 622592,
+ "version": "5.3.6",
+ "index": "96",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOCDStorageFamily": {
+ "size": 32768,
+ "version": "1.5",
+ "index": "55",
+ "refcount": "3"
+ },
+ "com.apple.driver.AppleUSBEHCI": {
+ "size": 73728,
+ "version": "3.2.5",
+ "index": "39",
+ "refcount": "0"
+ },
+ "com.apple.nvidia.nv50hal": {
+ "size": 2445312,
+ "version": "5.3.6",
+ "index": "91",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleSMBIOS": {
+ "size": 16384,
+ "version": "1.1.1",
+ "index": "29",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleBacklight": {
+ "size": 16384,
+ "version": "1.4.4",
+ "index": "72",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleACPIPlatform": {
+ "size": 253952,
+ "version": "1.2.4",
+ "index": "19",
+ "refcount": "3"
+ },
+ "com.apple.iokit.SCSITaskUserClient": {
+ "size": 24576,
+ "version": "2.0.5",
+ "index": "54",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOHIDFamily": {
+ "size": 233472,
+ "version": "1.5.3",
+ "index": "21",
+ "refcount": "7"
+ },
+ "com.apple.driver.DiskImages": {
+ "size": 65536,
+ "version": "195.2.2",
+ "index": "101",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IODVDStorageFamily": {
+ "size": 24576,
+ "version": "1.5",
+ "index": "56",
+ "refcount": "2"
+ },
+ "com.apple.driver.XsanFilter": {
+ "size": 20480,
+ "version": "2.7.91",
+ "index": "53",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleEFIRuntime": {
+ "size": 12288,
+ "version": "1.2.0",
+ "index": "35",
+ "refcount": "1"
+ },
+ "com.apple.driver.AppleRTC": {
+ "size": 20480,
+ "version": "1.2.3",
+ "index": "34",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOFireWireIP": {
+ "size": 36864,
+ "version": "1.7.6",
+ "index": "83",
+ "refcount": "0"
+ },
+ "com.vmware.kext.vmci": {
+ "size": 45056,
+ "version": "2.0.4",
+ "index": "106",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IO80211Family": {
+ "size": 126976,
+ "version": "215.1",
+ "index": "87",
+ "refcount": "1"
+ },
+ "com.apple.nke.applicationfirewall": {
+ "size": 32768,
+ "version": "1.0.77",
+ "index": "24",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOAHCIBlockStorage": {
+ "size": 69632,
+ "version": "1.2.0",
+ "index": "48",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleUSBUHCI": {
+ "size": 57344,
+ "version": "3.2.5",
+ "index": "38",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOAHCIFamily": {
+ "size": 24576,
+ "version": "1.5.0",
+ "index": "42",
+ "refcount": "2"
+ },
+ "com.apple.driver.AppleAHCIPort": {
+ "size": 53248,
+ "version": "1.5.2",
+ "index": "43",
+ "refcount": "0"
+ },
+ "com.apple.driver.AppleEFINVRAM": {
+ "size": 24576,
+ "version": "1.2.0",
+ "index": "36",
+ "refcount": "0"
+ },
+ "com.apple.iokit.IOUSBFamily": {
+ "size": 167936,
+ "version": "3.2.7",
+ "index": "37",
+ "refcount": "13"
+ },
+ "com.apple.driver.AppleUSBMergeNub": {
+ "size": 12288,
+ "version": "3.2.4",
+ "index": "61",
+ "refcount": "0"
+ }
+ },
+ "machine": "i386",
+ "name": "Darwin",
+ "os": "Darwin",
+ "version": "Darwin Kernel Version 9.6.0: Mon Nov 24 17:37:00 PST 2008; root:xnu-1228.9.59~1\/RELEASE_I386",
+ "release": "9.6.0"
+ },
+ "platform_version": "10.5.6",
+ "platform": "mac_os_x",
+ "ipaddress": "192.168.88.1",
+ "keys": {
+ "ssh": {
+ "host_dsa_public": "private",
+ "host_rsa_public": "private"
+ }
+ },
+ "network": {
+ "settings": {
+ "net.inet6.ip6.forwarding": "0",
+ "net.inet.ip.dummynet.debug": "0",
+ "net.inet.ip.rtexpire": "10",
+ "net.inet6.ipsec6.esp_trans_deflev": "1",
+ "net.inet.tcp.tcbhashsize": "4096",
+ "net.key.esp_auth": "0",
+ "net.inet6.ip6.hlim": "64",
+ "net.inet.ip.fw.dyn_fin_lifetime": "1",
+ "net.inet.ip.fw.dyn_udp_lifetime": "10",
+ "net.inet.icmp.bmcastecho": "1",
+ "net.athforceBias": "2 2",
+ "net.athbgscan": "1 1",
+ "net.inet.tcp.reass.maxsegments": "2048",
+ "net.inet6.ip6.auto_flowlabel": "1",
+ "net.inet6.ip6.rtmaxcache": "128",
+ "net.inet.tcp.sendspace": "131072",
+ "net.inet.tcp.keepinit": "75000",
+ "net.inet.ip.dummynet.max_chain_len": "16",
+ "net.inet.tcp.rfc1644": "0",
+ "net.inet.ip.fw.curr_dyn_buckets": "256",
+ "net.inet.ip.dummynet.ready_heap": "0",
+ "net.inet.ip.portrange.first": "49152",
+ "net.inet.tcp.background_io_trigger": "5",
+ "net.link.ether.inet.host_down_time": "20",
+ "net.inet6.ipsec6.def_policy": "1",
+ "net.inet6.ipsec6.ecn": "0",
+ "net.inet.ip.fastforwarding": "0",
+ "net.athaddbaignore": "0 0",
+ "net.inet6.ip6.v6only": "0",
+ "net.inet.tcp.sack": "1",
+ "net.inet6.ip6.rtexpire": "3600",
+ "net.link.ether.inet.proxyall": "0",
+ "net.inet6.ip6.keepfaith": "0",
+ "net.key.spi_trycnt": "1000",
+ "net.link.ether.inet.prune_intvl": "300",
+ "net.inet.tcp.ecn_initiate_out": "0",
+ "net.inet.ip.fw.dyn_rst_lifetime": "1",
+ "net.local.stream.sendspace": "8192",
+ "net.inet.tcp.socket_unlocked_on_output": "1",
+ "net.inet.ip.fw.verbose_limit": "0",
+ "net.local.dgram.recvspace": "4096",
+ "net.inet.ipsec.debug": "0",
+ "net.link.ether.inet.log_arp_warnings": "0",
+ "net.inet.tcp.ecn_negotiate_in": "0",
+ "net.inet.tcp.rfc3465": "1",
+ "net.inet.tcp.icmp_may_rst": "1",
+ "net.link.ether.inet.sendllconflict": "0",
+ "net.inet.ipsec.ah_offsetmask": "0",
+ "net.key.blockacq_count": "10",
+ "net.inet.tcp.delayed_ack": "3",
+ "net.inet.ip.fw.verbose": "2",
+ "net.inet.ip.fw.dyn_count": "0",
+ "net.inet.tcp.slowlink_wsize": "8192",
+ "net.inet6.ip6.fw.enable": "1",
+ "net.inet.ip.portrange.hilast": "65535",
+ "net.inet.icmp.maskrepl": "0",
+ "net.link.ether.inet.apple_hwcksum_rx": "1",
+ "net.inet.tcp.drop_synfin": "1",
+ "net.key.spi_maxval": "268435455",
+ "net.inet.ipsec.ecn": "0",
+ "net.inet.ip.fw.dyn_keepalive": "1",
+ "net.key.int_random": "60",
+ "net.key.debug": "0",
+ "net.inet.ip.dummynet.curr_time": "0",
+ "net.inet.udp.blackhole": "0",
+ "net.athaggrqmin": "1 1",
+ "net.athppmenable": "1 1",
+ "net.inet.ip.fw.dyn_syn_lifetime": "20",
+ "net.inet.tcp.keepidle": "7200000",
+ "net.inet6.ip6.tempvltime": "604800",
+ "net.inet.tcp.recvspace": "358400",
+ "net.inet.tcp.keepintvl": "75000",
+ "net.inet.udp.maxdgram": "9216",
+ "net.inet.ip.maxchainsent": "0",
+ "net.inet.ipsec.esp_net_deflev": "1",
+ "net.inet6.icmp6.nd6_useloopback": "1",
+ "net.inet.tcp.slowstart_flightsize": "1",
+ "net.inet.ip.fw.debug": "0",
+ "net.inet.ip.linklocal.in.allowbadttl": "1",
+ "net.key.spi_minval": "256",
+ "net.inet.ip.forwarding": "0",
+ "net.inet.tcp.v6mssdflt": "1024",
+ "net.key.larval_lifetime": "30",
+ "net.inet6.ip6.fw.verbose_limit": "0",
+ "net.inet.ip.dummynet.red_lookup_depth": "256",
+ "net.inet.tcp.pcbcount": "36",
+ "net.inet.ip.fw.dyn_ack_lifetime": "300",
+ "net.inet.ip.portrange.lowlast": "600",
+ "net.athCCAThreshold": "28 28",
+ "net.link.ether.inet.useloopback": "1",
+ "net.athqdepth": "0 0",
+ "net.inet.ip.ttl": "64",
+ "net.inet.ip.rtmaxcache": "128",
+ "net.inet.ipsec.bypass": "0",
+ "net.inet6.icmp6.nd6_debug": "0",
+ "net.inet.ip.use_route_genid": "1",
+ "net.inet6.icmp6.rediraccept": "1",
+ "net.inet.ip.fw.static_count": "1",
+ "net.inet6.ip6.fw.debug": "0",
+ "net.inet.udp.pcbcount": "104",
+ "net.inet.ipsec.esp_randpad": "-1",
+ "net.inet6.icmp6.nd6_maxnudhint": "0",
+ "net.inet.tcp.always_keepalive": "0",
+ "net.inet.udp.checksum": "1",
+ "net.link.ether.inet.keep_announcements": "1",
+ "net.athfixedDropThresh": "150 150",
+ "net.inet6.ip6.kame_version": "20010528\/apple-darwin",
+ "net.inet.ip.fw.dyn_max": "4096",
+ "net.inet.udp.log_in_vain": "0",
+ "net.inet6.icmp6.nd6_mmaxtries": "3",
+ "net.inet.ip.rtminexpire": "10",
+ "net.inet.ip.fw.dyn_buckets": "256",
+ "net.inet6.ip6.accept_rtadv": "0",
+ "net.inet6.ip6.rr_prune": "5",
+ "net.key.ah_keymin": "128",
+ "net.inet.ip.redirect": "1",
+ "net.inet.tcp.sack_globalmaxholes": "65536",
+ "net.inet.ip.keepfaith": "0",
+ "net.inet.ip.dummynet.expire": "1",
+ "net.inet.ip.gifttl": "30",
+ "net.inet.ip.portrange.last": "65535",
+ "net.inet.ipsec.ah_net_deflev": "1",
+ "net.inet6.icmp6.nd6_delay": "5",
+ "net.inet.tcp.packetchain": "50",
+ "net.inet6.ip6.hdrnestlimit": "50",
+ "net.inet.tcp.newreno": "0",
+ "net.inet6.ip6.dad_count": "1",
+ "net.inet6.ip6.auto_linklocal": "1",
+ "net.inet6.ip6.temppltime": "86400",
+ "net.inet.tcp.strict_rfc1948": "0",
+ "net.athdupie": "1 1",
+ "net.inet.ip.dummynet.red_max_pkt_size": "1500",
+ "net.inet.ip.maxfrags": "2048",
+ "net.inet.tcp.log_in_vain": "0",
+ "net.inet.tcp.rfc1323": "1",
+ "net.inet.ip.subnets_are_local": "0",
+ "net.inet.ip.dummynet.search_steps": "0",
+ "net.inet.icmp.icmplim": "250",
+ "net.link.ether.inet.apple_hwcksum_tx": "1",
+ "net.inet6.icmp6.redirtimeout": "600",
+ "net.inet.ipsec.ah_cleartos": "1",
+ "net.inet6.ip6.log_interval": "5",
+ "net.link.ether.inet.max_age": "1200",
+ "net.inet.ip.fw.enable": "1",
+ "net.inet6.ip6.redirect": "1",
+ "net.athaggrfmax": "28 28",
+ "net.inet.ip.maxfragsperpacket": "128",
+ "net.inet6.ip6.use_deprecated": "1",
+ "net.link.generic.system.dlil_input_sanity_check": "0",
+ "net.inet.tcp.sack_globalholes": "0",
+ "net.inet.tcp.reass.cursegments": "0",
+ "net.inet6.icmp6.nodeinfo": "3",
+ "net.local.inflight": "0",
+ "net.inet.ip.dummynet.hash_size": "64",
+ "net.inet.ip.dummynet.red_avg_pkt_size": "512",
+ "net.inet.ipsec.dfbit": "0",
+ "net.inet.tcp.reass.overflows": "0",
+ "net.inet.tcp.rexmt_thresh": "2",
+ "net.inet6.ip6.maxfrags": "8192",
+ "net.inet6.ip6.rtminexpire": "10",
+ "net.inet6.ipsec6.esp_net_deflev": "1",
+ "net.inet.tcp.blackhole": "0",
+ "net.key.esp_keymin": "256",
+ "net.inet.ip.check_interface": "0",
+ "net.inet.tcp.minmssoverload": "0",
+ "net.link.ether.inet.maxtries": "5",
+ "net.inet.tcp.do_tcpdrain": "0",
+ "net.inet.ipsec.esp_port": "4500",
+ "net.inet6.ipsec6.ah_net_deflev": "1",
+ "net.inet.ip.dummynet.extract_heap": "0",
+ "net.inet.tcp.path_mtu_discovery": "1",
+ "net.inet.ip.intr_queue_maxlen": "50",
+ "net.inet.ipsec.def_policy": "1",
+ "net.inet.ip.fw.autoinc_step": "100",
+ "net.inet.ip.accept_sourceroute": "0",
+ "net.inet.raw.maxdgram": "8192",
+ "net.inet.ip.maxfragpackets": "1024",
+ "net.inet.ip.fw.one_pass": "0",
+ "net.appletalk.routermix": "2000",
+ "net.inet.tcp.tcp_lq_overflow": "1",
+ "net.link.generic.system.ifcount": "9",
+ "net.link.ether.inet.send_conflicting_probes": "1",
+ "net.inet.tcp.background_io_enabled": "1",
+ "net.inet6.ipsec6.debug": "0",
+ "net.inet.tcp.win_scale_factor": "3",
+ "net.key.natt_keepalive_interval": "20",
+ "net.inet.tcp.msl": "15000",
+ "net.inet.ip.portrange.hifirst": "49152",
+ "net.inet.ipsec.ah_trans_deflev": "1",
+ "net.inet.tcp.rtt_min": "1",
+ "net.inet6.ip6.defmcasthlim": "1",
+ "net.inet6.icmp6.nd6_prune": "1",
+ "net.inet6.ip6.fw.verbose": "0",
+ "net.inet.ip.portrange.lowfirst": "1023",
+ "net.inet.tcp.maxseg_unacked": "8",
+ "net.local.dgram.maxdgram": "2048",
+ "net.key.blockacq_lifetime": "20",
+ "net.inet.tcp.sack_maxholes": "128",
+ "net.inet6.ip6.maxfragpackets": "1024",
+ "net.inet6.ip6.use_tempaddr": "0",
+ "net.athpowermode": "0 0",
+ "net.inet.udp.recvspace": "73728",
+ "net.inet.tcp.isn_reseed_interval": "0",
+ "net.inet.tcp.local_slowstart_flightsize": "8",
+ "net.inet.ip.dummynet.searches": "0",
+ "net.inet.ip.intr_queue_drops": "0",
+ "net.link.generic.system.multi_threaded_input": "1",
+ "net.inet.raw.recvspace": "8192",
+ "net.inet.ipsec.esp_trans_deflev": "1",
+ "net.key.prefered_oldsa": "0",
+ "net.local.stream.recvspace": "8192",
+ "net.inet.tcp.sockthreshold": "64",
+ "net.inet6.icmp6.nd6_umaxtries": "3",
+ "net.pstimeout": "20 20",
+ "net.inet.ip.sourceroute": "0",
+ "net.inet.ip.fw.dyn_short_lifetime": "5",
+ "net.inet.tcp.minmss": "216",
+ "net.inet6.ip6.gifhlim": "0",
+ "net.athvendorie": "1 1",
+ "net.inet.ip.check_route_selfref": "1",
+ "net.inet6.icmp6.errppslimit": "100",
+ "net.inet.tcp.mssdflt": "512",
+ "net.inet.icmp.log_redirect": "0",
+ "net.inet6.ipsec6.ah_trans_deflev": "1",
+ "net.inet6.ipsec6.esp_randpad": "-1",
+ "net.inet.icmp.drop_redirect": "0",
+ "net.inet.icmp.timestamp": "0",
+ "net.inet.ip.random_id": "1"
+ },
+ "interfaces": {
+ "vmnet1": {
+ "flags": [
+ "UP",
+ "BROADCAST",
+ "SMART",
+ "RUNNING",
+ "SIMPLEX",
+ "MULTICAST"
+ ],
+ "addresses": [
+ {
+ "broadcast": "192.168.88.255",
+ "netmask": "255.255.255.0",
+ "family": "inet",
+ "address": "192.168.88.1"
+ },
+ {
+ "family": "lladdr",
+ "address": "private"
+ }
+ ],
+ "number": "1",
+ "mtu": "1500",
+ "type": "vmnet",
+ "encapsulation": "Ethernet"
+ },
+ "stf0": {
+ "flags": [
+
+ ],
+ "number": "0",
+ "mtu": "1280",
+ "type": "stf",
+ "encapsulation": "6to4"
+ },
+ "vboxnet0": {
+ "flags": [
+ "BROADCAST",
+ "RUNNING",
+ "SIMPLEX",
+ "MULTICAST"
+ ],
+ "addresses": [
+ {
+ "family": "lladdr",
+ "address": "private"
+ }
+ ],
+ "number": "0",
+ "mtu": "1500",
+ "type": "vboxnet",
+ "encapsulation": "Ethernet"
+ },
+ "lo0": {
+ "flags": [
+ "UP",
+ "LOOPBACK",
+ "RUNNING",
+ "MULTICAST"
+ ],
+ "addresses": [
+ {
+ "scope": "Link",
+ "prefixlen": "64",
+ "family": "inet6",
+ "address": "fe80::1"
+ },
+ {
+ "netmask": "255.0.0.0",
+ "family": "inet",
+ "address": "127.0.0.1"
+ },
+ {
+ "scope": "Node",
+ "prefixlen": "128",
+ "family": "inet6",
+ "address": "::1"
+ },
+ {
+ "scope": "Node",
+ "prefixlen": "128",
+ "family": "inet6",
+ "address": "private"
+ }
+ ],
+ "number": "0",
+ "mtu": "16384",
+ "type": "lo",
+ "encapsulation": "Loopback"
+ },
+ "vboxn": {
+ "counters": {
+ "tx": {
+ "bytes": "0",
+ "packets": "0",
+ "collisions": "0",
+ "compressed": 0,
+ "carrier": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0
+ },
+ "rx": {
+ "bytes": "0",
+ "packets": "0",
+ "compressed": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0,
+ "frame": 0,
+ "multicast": 0
+ }
+ }
+ },
+ "gif0": {
+ "flags": [
+ "POINTOPOINT",
+ "MULTICAST"
+ ],
+ "number": "0",
+ "mtu": "1280",
+ "type": "gif",
+ "encapsulation": "IPIP"
+ },
+ "vmnet": {
+ "counters": {
+ "tx": {
+ "bytes": "0",
+ "packets": "0",
+ "collisions": "0",
+ "compressed": 0,
+ "carrier": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0
+ },
+ "rx": {
+ "bytes": "0",
+ "packets": "0",
+ "compressed": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0,
+ "frame": 0,
+ "multicast": 0
+ }
+ }
+ },
+ "vmnet8": {
+ "flags": [
+ "UP",
+ "BROADCAST",
+ "SMART",
+ "RUNNING",
+ "SIMPLEX",
+ "MULTICAST"
+ ],
+ "addresses": [
+ {
+ "broadcast": "192.168.237.255",
+ "netmask": "255.255.255.0",
+ "family": "inet",
+ "address": "192.168.237.1"
+ },
+ {
+ "family": "lladdr",
+ "address": "private"
+ }
+ ],
+ "number": "8",
+ "mtu": "1500",
+ "type": "vmnet",
+ "encapsulation": "Ethernet"
+ },
+ "en0": {
+ "status": "inactive",
+ "flags": [
+ "UP",
+ "BROADCAST",
+ "SMART",
+ "RUNNING",
+ "SIMPLEX",
+ "MULTICAST"
+ ],
+ "addresses": [
+ {
+ "family": "lladdr",
+ "address": "private"
+ }
+ ],
+ "number": "0",
+ "mtu": "1500",
+ "media": {
+ "supported": [
+ {
+ "autoselect": {
+ "options": [
+
+ ]
+ }
+ },
+ {
+ "10baseT\/UTP": {
+ "options": [
+ "half-duplex"
+ ]
+ }
+ },
+ {
+ "10baseT\/UTP": {
+ "options": [
+ "full-duplex"
+ ]
+ }
+ },
+ {
+ "10baseT\/UTP": {
+ "options": [
+ "full-duplex",
+ "hw-loopback"
+ ]
+ }
+ },
+ {
+ "10baseT\/UTP": {
+ "options": [
+ "full-duplex",
+ "flow-control"
+ ]
+ }
+ },
+ {
+ "100baseTX": {
+ "options": [
+ "half-duplex"
+ ]
+ }
+ },
+ {
+ "100baseTX": {
+ "options": [
+ "full-duplex"
+ ]
+ }
+ },
+ {
+ "100baseTX": {
+ "options": [
+ "full-duplex",
+ "hw-loopback"
+ ]
+ }
+ },
+ {
+ "100baseTX": {
+ "options": [
+ "full-duplex",
+ "flow-control"
+ ]
+ }
+ },
+ {
+ "1000baseT": {
+ "options": [
+ "full-duplex"
+ ]
+ }
+ },
+ {
+ "1000baseT": {
+ "options": [
+ "full-duplex",
+ "hw-loopback"
+ ]
+ }
+ },
+ {
+ "1000baseT": {
+ "options": [
+ "full-duplex",
+ "flow-control"
+ ]
+ }
+ },
+ {
+ "none": {
+ "options": [
+
+ ]
+ }
+ }
+ ],
+ "selected": [
+ {
+ "autoselect": {
+ "options": [
+
+ ]
+ }
+ }
+ ]
+ },
+ "type": "en",
+ "counters": {
+ "tx": {
+ "bytes": "342",
+ "packets": "0",
+ "collisions": "0",
+ "compressed": 0,
+ "carrier": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0
+ },
+ "rx": {
+ "bytes": "0",
+ "packets": "0",
+ "compressed": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0,
+ "frame": 0,
+ "multicast": 0
+ }
+ },
+ "encapsulation": "Ethernet"
+ },
+ "en1": {
+ "status": "active",
+ "flags": [
+ "UP",
+ "BROADCAST",
+ "SMART",
+ "RUNNING",
+ "SIMPLEX",
+ "MULTICAST"
+ ],
+ "addresses": [
+ {
+ "scope": "Link",
+ "prefixlen": "64",
+ "family": "inet6",
+ "address": "private"
+ },
+ {
+ "broadcast": "192.168.1.255",
+ "netmask": "255.255.255.0",
+ "family": "inet",
+ "address": "192.168.1.4"
+ },
+ {
+ "family": "lladdr",
+ "address": "private"
+ }
+ ],
+ "number": "1",
+ "mtu": "1500",
+ "media": {
+ "supported": [
+ {
+ "autoselect": {
+ "options": [
+
+ ]
+ }
+ }
+ ],
+ "selected": [
+ {
+ "autoselect": {
+ "options": [
+
+ ]
+ }
+ }
+ ]
+ },
+ "type": "en",
+ "counters": {
+ "tx": {
+ "bytes": "449206298",
+ "packets": "7041789",
+ "collisions": "0",
+ "compressed": 0,
+ "carrier": 0,
+ "drop": 0,
+ "errors": "95",
+ "overrun": 0
+ },
+ "rx": {
+ "bytes": "13673879120",
+ "packets": "19966002",
+ "compressed": 0,
+ "drop": 0,
+ "errors": "1655893",
+ "overrun": 0,
+ "frame": 0,
+ "multicast": 0
+ }
+ },
+ "arp": {
+ "192.168.1.7": "private"
+ },
+ "encapsulation": "Ethernet"
+ },
+ "fw0": {
+ "status": "inactive",
+ "flags": [
+ "UP",
+ "BROADCAST",
+ "SMART",
+ "RUNNING",
+ "SIMPLEX",
+ "MULTICAST"
+ ],
+ "addresses": [
+ {
+ "family": "lladdr",
+ "address": "private"
+ }
+ ],
+ "number": "0",
+ "mtu": "4078",
+ "media": {
+ "supported": [
+ {
+ "autoselect": {
+ "options": [
+ "full-duplex"
+ ]
+ }
+ }
+ ],
+ "selected": [
+ {
+ "autoselect": {
+ "options": [
+ "full-duplex"
+ ]
+ }
+ }
+ ]
+ },
+ "type": "fw",
+ "counters": {
+ "tx": {
+ "bytes": "346",
+ "packets": "0",
+ "collisions": "0",
+ "compressed": 0,
+ "carrier": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0
+ },
+ "rx": {
+ "bytes": "0",
+ "packets": "0",
+ "compressed": 0,
+ "drop": 0,
+ "errors": "0",
+ "overrun": 0,
+ "frame": 0,
+ "multicast": 0
+ }
+ },
+ "encapsulation": "1394"
+ }
+ }
+ },
+ "fqdn": "local.local",
+ "ohai_time": 1240624355.08575,
+ "domain": "local",
+ "os": "darwin",
+ "platform_build": "9G55",
+ "os_version": "9.6.0",
+ "hostname": "local",
+ "macaddress": "private",
+ "languages": {
+ "ruby": {
+ "target_os": "darwin9.0",
+ "platform": "universal-darwin9.0",
+ "host_vendor": "apple",
+ "target_vendor": "apple",
+ "target_cpu": "i686",
+ "host_os": "darwin9.0",
+ "host_cpu": "i686",
+ "version": "1.8.6",
+ "host": "i686-apple-darwin9.0",
+ "target": "i686-apple-darwin9.0",
+ "release_date": "2008-03-03"
+ }
+ }
+}
diff --git a/benchmarks/ohai.ruby b/benchmarks/ohai.ruby
new file mode 100644
index 0000000..36b4297
--- /dev/null
+++ b/benchmarks/ohai.ruby
@@ -0,0 +1 @@
+{"kernel"=>{"name"=>"Darwin", "machine"=>"i386", "modules"=>{"com.apple.driver.AppleAPIC"=>{"size"=>12288, "version"=>"1.4", "index"=>"26", "refcount"=>"0"}, "com.apple.driver.AirPort.Atheros"=>{"size"=>593920, "version"=>"318.8.3", "index"=>"88", "refcount"=>"0"}, "com.apple.driver.AppleIntelCPUPowerManagement"=>{"size"=>102400, "version"=>"59.0.1", "index"=>"22", "refcount"=>"0"}, "com.apple.iokit.IOStorageFamily"=>{"size"=>98304, "version"=>"1.5.5", "index"=>"44", "refcount"=>"9"}, "com.apple.iokit.IOATAPIProtocolTransport"=>{"size"=>16384, "version"=>"1.5.2", "index"=>"52", "refcount"=>"0"}, "com.apple.iokit.IOPCIFamily"=>{"size"=>65536, "version"=>"2.5", "index"=>"17", "refcount"=>"18"}, "org.virtualbox.kext.VBoxDrv"=>{"size"=>118784, "version"=>"2.2.0", "index"=>"114", "refcount"=>"3"}, "com.cisco.nke.ipsec"=>{"size"=>454656, "version"=>"2.0.1", "index"=>"111", "refcount"=>"0"}, "com.apple.driver.AppleHPET"=>{"size"=>12288, "version"=>"1.3", "index"=>"33", "refcount"=>"0"}, "com.apple.driver.AppleUSBHub"=>{"size"=>49152, "version"=>"3.2.7", "index"=>"47", "refcount"=>"0"}, "com.apple.iokit.IOFireWireFamily"=>{"size"=>258048, "version"=>"3.4.6", "index"=>"49", "refcount"=>"2"}, "com.apple.driver.AppleUSBComposite"=>{"size"=>16384, "version"=>"3.2.0", "index"=>"60", "refcount"=>"1"}, "com.apple.driver.AppleIntelPIIXATA"=>{"size"=>36864, "version"=>"2.0.0", "index"=>"41", "refcount"=>"0"}, "com.apple.driver.AppleSmartBatteryManager"=>{"size"=>28672, "version"=>"158.6.0", "index"=>"32", "refcount"=>"0"}, "com.apple.filesystems.udf"=>{"size"=>233472, "version"=>"2.0.2", "index"=>"119", "refcount"=>"0"}, "com.apple.iokit.IOSMBusFamily"=>{"size"=>12288, "version"=>"1.1", "index"=>"27", "refcount"=>"2"}, "com.apple.iokit.IOACPIFamily"=>{"size"=>16384, "version"=>"1.2.0", "index"=>"18", "refcount"=>"10"}, "foo.tap"=>{"size"=>24576, "version"=>"1.0", "index"=>"113", "refcount"=>"0"}, "com.vmware.kext.vmx86"=>{"size"=>864256, "version"=>"2.0.4", "index"=>"104", "refcount"=>"0"}, "com.apple.iokit.CHUDUtils"=>{"size"=>28672, "version"=>"200", "index"=>"98", "refcount"=>"0"}, "com.apple.driver.AppleACPIButtons"=>{"size"=>16384, "version"=>"1.2.4", "index"=>"30", "refcount"=>"0"}, "com.apple.driver.AppleFWOHCI"=>{"size"=>139264, "version"=>"3.7.2", "index"=>"50", "refcount"=>"0"}, "com.apple.iokit.IOSCSIArchitectureModelFamily"=>{"size"=>102400, "version"=>"2.0.5", "index"=>"51", "refcount"=>"4"}, "org.virtualbox.kext.VBoxNetAdp"=>{"size"=>8192, "version"=>"2.2.0", "index"=>"117", "refcount"=>"0"}, "com.apple.filesystems.autofs"=>{"size"=>45056, "version"=>"2.0.1", "index"=>"109", "refcount"=>"0"}, "com.vmware.kext.vmnet"=>{"size"=>36864, "version"=>"2.0.4", "index"=>"108", "refcount"=>"0"}, "com.apple.iokit.IOSCSIBlockCommandsDevice"=>{"size"=>90112, "version"=>"2.0.5", "index"=>"57", "refcount"=>"1"}, "com.apple.driver.AppleACPIPCI"=>{"size"=>12288, "version"=>"1.2.4", "index"=>"31", "refcount"=>"0"}, "com.apple.security.seatbelt"=>{"size"=>98304, "version"=>"107.10", "index"=>"25", "refcount"=>"0"}, "com.apple.driver.AppleUpstreamUserClient"=>{"size"=>16384, "version"=>"2.7.2", "index"=>"100", "refcount"=>"0"}, "com.apple.kext.OSvKernDSPLib"=>{"size"=>12288, "version"=>"1.1", "index"=>"79", "refcount"=>"1"}, "com.apple.iokit.IOBDStorageFamily"=>{"size"=>20480, "version"=>"1.5", "index"=>"58", "refcount"=>"1"}, "com.apple.iokit.IOGraphicsFamily"=>{"size"=>118784, "version"=>"1.7.1", "index"=>"70", "refcount"=>"5"}, "com.apple.iokit.IONetworkingFamily"=>{"size"=>90112, "version"=>"1.6.1", "index"=>"82", "refcount"=>"4"}, "com.apple.iokit.IOATAFamily"=>{"size"=>53248, "version"=>"2.0.0", "index"=>"40", "refcount"=>"2"}, "com.apple.iokit.IOUSBHIDDriver"=>{"size"=>20480, "version"=>"3.2.2", "index"=>"63", "refcount"=>"2"}, "org.virtualbox.kext.VBoxUSB"=>{"size"=>28672, "version"=>"2.2.0", "index"=>"115", "refcount"=>"0"}, "com.apple.security.TMSafetyNet"=>{"size"=>12288, "version"=>"3", "index"=>"23", "refcount"=>"0"}, "com.apple.iokit.IONDRVSupport"=>{"size"=>57344, "version"=>"1.7.1", "index"=>"71", "refcount"=>"3"}, "com.apple.BootCache"=>{"size"=>20480, "version"=>"30.3", "index"=>"20", "refcount"=>"0"}, "com.vmware.kext.vmioplug"=>{"size"=>24576, "version"=>"2.0.4", "index"=>"107", "refcount"=>"0"}, "com.apple.iokit.IOUSBUserClient"=>{"size"=>8192, "version"=>"3.2.4", "index"=>"46", "refcount"=>"1"}, "com.apple.iokit.IOSCSIMultimediaCommandsDevice"=>{"size"=>90112, "version"=>"2.0.5", "index"=>"59", "refcount"=>"0"}, "com.apple.driver.AppleIRController"=>{"size"=>20480, "version"=>"110", "index"=>"78", "refcount"=>"0"}, "com.apple.driver.AudioIPCDriver"=>{"size"=>16384, "version"=>"1.0.5", "index"=>"81", "refcount"=>"0"}, "com.apple.driver.AppleLPC"=>{"size"=>12288, "version"=>"1.2.11", "index"=>"73", "refcount"=>"0"}, "org.virtualbox.kext.VBoxNetFlt"=>{"size"=>16384, "version"=>"2.2.0", "index"=>"116", "refcount"=>"0"}, "com.apple.iokit.CHUDKernLib"=>{"size"=>20480, "version"=>"196", "index"=>"93", "refcount"=>"2"}, "com.apple.iokit.CHUDProf"=>{"size"=>49152, "version"=>"207", "index"=>"97", "refcount"=>"0"}, "com.apple.NVDAResman"=>{"size"=>2478080, "version"=>"5.3.6", "index"=>"90", "refcount"=>"2"}, "com.apple.driver.AppleACPIEC"=>{"size"=>20480, "version"=>"1.2.4", "index"=>"28", "refcount"=>"0"}, "foo.tun"=>{"size"=>24576, "version"=>"1.0", "index"=>"118", "refcount"=>"0"}, "com.apple.iokit.IOSerialFamily"=>{"size"=>36864, "version"=>"9.3", "index"=>"102", "refcount"=>"1"}, "com.apple.GeForce"=>{"size"=>622592, "version"=>"5.3.6", "index"=>"96", "refcount"=>"0"}, "com.apple.iokit.IOCDStorageFamily"=>{"size"=>32768, "version"=>"1.5", "index"=>"55", "refcount"=>"3"}, "com.apple.driver.AppleUSBEHCI"=>{"size"=>73728, "version"=>"3.2.5", "index"=>"39", "refcount"=>"0"}, "com.apple.nvidia.nv50hal"=>{"size"=>2445312, "version"=>"5.3.6", "index"=>"91", "refcount"=>"0"}, "com.apple.driver.AppleSMBIOS"=>{"size"=>16384, "version"=>"1.1.1", "index"=>"29", "refcount"=>"0"}, "com.apple.driver.AppleBacklight"=>{"size"=>16384, "version"=>"1.4.4", "index"=>"72", "refcount"=>"0"}, "com.apple.driver.AppleACPIPlatform"=>{"size"=>253952, "version"=>"1.2.4", "index"=>"19", "refcount"=>"3"}, "com.apple.iokit.SCSITaskUserClient"=>{"size"=>24576, "version"=>"2.0.5", "index"=>"54", "refcount"=>"0"}, "com.apple.iokit.IOHIDFamily"=>{"size"=>233472, "version"=>"1.5.3", "index"=>"21", "refcount"=>"7"}, "com.apple.driver.DiskImages"=>{"size"=>65536, "version"=>"195.2.2", "index"=>"101", "refcount"=>"0"}, "com.apple.iokit.IODVDStorageFamily"=>{"size"=>24576, "version"=>"1.5", "index"=>"56", "refcount"=>"2"}, "com.apple.iokit.IOFireWireIP"=>{"size"=>36864, "version"=>"1.7.6", "index"=>"83", "refcount"=>"0"}, "com.apple.driver.AppleRTC"=>{"size"=>20480, "version"=>"1.2.3", "index"=>"34", "refcount"=>"0"}, "com.apple.driver.XsanFilter"=>{"size"=>20480, "version"=>"2.7.91", "index"=>"53", "refcount"=>"0"}, "com.apple.driver.AppleEFIRuntime"=>{"size"=>12288, "version"=>"1.2.0", "index"=>"35", "refcount"=>"1"}, "com.apple.iokit.IOAHCIBlockStorage"=>{"size"=>69632, "version"=>"1.2.0", "index"=>"48", "refcount"=>"0"}, "com.apple.nke.applicationfirewall"=>{"size"=>32768, "version"=>"1.0.77", "index"=>"24", "refcount"=>"0"}, "com.apple.iokit.IO80211Family"=>{"size"=>126976, "version"=>"215.1", "index"=>"87", "refcount"=>"1"}, "com.vmware.kext.vmci"=>{"size"=>45056, "version"=>"2.0.4", "index"=>"106", "refcount"=>"0"}, "com.apple.iokit.IOAHCIFamily"=>{"size"=>24576, "version"=>"1.5.0", "index"=>"42", "refcount"=>"2"}, "com.apple.driver.AppleUSBUHCI"=>{"size"=>57344, "version"=>"3.2.5", "index"=>"38", "refcount"=>"0"}, "com.apple.driver.AppleUSBMergeNub"=>{"size"=>12288, "version"=>"3.2.4", "index"=>"61", "refcount"=>"0"}, "com.apple.iokit.IOUSBFamily"=>{"size"=>167936, "version"=>"3.2.7", "index"=>"37", "refcount"=>"13"}, "com.apple.driver.AppleEFINVRAM"=>{"size"=>24576, "version"=>"1.2.0", "index"=>"36", "refcount"=>"0"}, "com.apple.driver.AppleAHCIPort"=>{"size"=>53248, "version"=>"1.5.2", "index"=>"43", "refcount"=>"0"}}, "os"=>"Darwin", "version"=>"Darwin Kernel Version 9.6.0: Mon Nov 24 17:37:00 PST 2008; root:xnu-1228.9.59~1/RELEASE_I386", "release"=>"9.6.0"}, "command"=>{"ps"=>"ps -ef"}, "platform"=>"mac_os_x", "platform_version"=>"10.5.6", "keys"=>{"ssh"=>{"host_dsa_public"=>"private", "host_rsa_public"=>"private"}}, "ipaddress"=>"192.168.88.1", "fqdn"=>"local.local", "network"=>{"settings"=>{"net.inet6.ip6.forwarding"=>"0", "net.inet.ip.dummynet.debug"=>"0", "net.inet.ip.rtexpire"=>"10", "net.inet6.ipsec6.esp_trans_deflev"=>"1", "net.inet.tcp.tcbhashsize"=>"4096", "net.key.esp_auth"=>"0", "net.inet6.ip6.hlim"=>"64", "net.inet.ip.fw.dyn_fin_lifetime"=>"1", "net.inet.ip.fw.dyn_udp_lifetime"=>"10", "net.inet.icmp.bmcastecho"=>"1", "net.athbgscan"=>"1 1", "net.inet.tcp.reass.maxsegments"=>"2048", "net.athforceBias"=>"2 2", "net.inet6.ip6.auto_flowlabel"=>"1", "net.inet6.ip6.rtmaxcache"=>"128", "net.inet.tcp.sendspace"=>"131072", "net.inet.tcp.keepinit"=>"75000", "net.inet.ip.dummynet.max_chain_len"=>"16", "net.inet.tcp.rfc1644"=>"0", "net.inet.ip.fw.curr_dyn_buckets"=>"256", "net.inet.ip.dummynet.ready_heap"=>"0", "net.inet.ip.portrange.first"=>"49152", "net.inet.tcp.background_io_trigger"=>"5", "net.link.ether.inet.host_down_time"=>"20", "net.inet6.ipsec6.def_policy"=>"1", "net.inet6.ipsec6.ecn"=>"0", "net.inet.ip.fastforwarding"=>"0", "net.inet6.ip6.v6only"=>"0", "net.inet.tcp.sack"=>"1", "net.inet6.ip6.rtexpire"=>"3600", "net.link.ether.inet.proxyall"=>"0", "net.athaddbaignore"=>"0 0", "net.inet6.ip6.keepfaith"=>"0", "net.key.spi_trycnt"=>"1000", "net.link.ether.inet.prune_intvl"=>"300", "net.inet.tcp.ecn_initiate_out"=>"0", "net.inet.ip.fw.dyn_rst_lifetime"=>"1", "net.local.stream.sendspace"=>"8192", "net.inet.tcp.socket_unlocked_on_output"=>"1", "net.inet.ip.fw.verbose_limit"=>"0", "net.local.dgram.recvspace"=>"4096", "net.inet.ipsec.debug"=>"0", "net.link.ether.inet.log_arp_warnings"=>"0", "net.inet.tcp.ecn_negotiate_in"=>"0", "net.inet.tcp.rfc3465"=>"1", "net.inet.tcp.icmp_may_rst"=>"1", "net.link.ether.inet.sendllconflict"=>"0", "net.inet.ipsec.ah_offsetmask"=>"0", "net.key.blockacq_count"=>"10", "net.inet.tcp.delayed_ack"=>"3", "net.inet.ip.fw.verbose"=>"2", "net.inet.ip.fw.dyn_count"=>"0", "net.inet.tcp.slowlink_wsize"=>"8192", "net.inet6.ip6.fw.enable"=>"1", "net.inet.ip.portrange.hilast"=>"65535", "net.inet.icmp.maskrepl"=>"0", "net.link.ether.inet.apple_hwcksum_rx"=>"1", "net.inet.tcp.drop_synfin"=>"1", "net.key.spi_maxval"=>"268435455", "net.inet.ipsec.ecn"=>"0", "net.inet.ip.fw.dyn_keepalive"=>"1", "net.key.int_random"=>"60", "net.key.debug"=>"0", "net.inet.ip.dummynet.curr_time"=>"0", "net.inet.udp.blackhole"=>"0", "net.athaggrqmin"=>"1 1", "net.inet.ip.fw.dyn_syn_lifetime"=>"20", "net.inet.tcp.keepidle"=>"7200000", "net.inet6.ip6.tempvltime"=>"604800", "net.inet.tcp.recvspace"=>"358400", "net.inet.udp.maxdgram"=>"9216", "net.inet.tcp.keepintvl"=>"75000", "net.inet.ip.maxchainsent"=>"0", "net.athppmenable"=>"1 1", "net.inet.ipsec.esp_net_deflev"=>"1", "net.inet6.icmp6.nd6_useloopback"=>"1", "net.inet.tcp.slowstart_flightsize"=>"1", "net.inet.ip.fw.debug"=>"0", "net.inet.ip.linklocal.in.allowbadttl"=>"1", "net.key.spi_minval"=>"256", "net.inet.ip.forwarding"=>"0", "net.inet.tcp.v6mssdflt"=>"1024", "net.key.larval_lifetime"=>"30", "net.inet6.ip6.fw.verbose_limit"=>"0", "net.inet.ip.dummynet.red_lookup_depth"=>"256", "net.inet.tcp.pcbcount"=>"36", "net.inet.ip.fw.dyn_ack_lifetime"=>"300", "net.athCCAThreshold"=>"28 28", "net.inet.ip.portrange.lowlast"=>"600", "net.link.ether.inet.useloopback"=>"1", "net.athqdepth"=>"0 0", "net.inet.ip.ttl"=>"64", "net.inet.ip.rtmaxcache"=>"128", "net.inet.ipsec.bypass"=>"0", "net.inet6.icmp6.nd6_debug"=>"0", "net.inet.ip.use_route_genid"=>"1", "net.inet6.icmp6.rediraccept"=>"1", "net.inet.ip.fw.static_count"=>"1", "net.inet6.ip6.fw.debug"=>"0", "net.inet.udp.pcbcount"=>"104", "net.inet.ipsec.esp_randpad"=>"-1", "net.inet6.icmp6.nd6_maxnudhint"=>"0", "net.inet.tcp.always_keepalive"=>"0", "net.inet.udp.checksum"=>"1", "net.link.ether.inet.keep_announcements"=>"1", "net.athfixedDropThresh"=>"150 150", "net.inet6.ip6.kame_version"=>"20010528/apple-darwin", "net.inet.ip.fw.dyn_max"=>"4096", "net.inet.udp.log_in_vain"=>"0", "net.inet6.icmp6.nd6_mmaxtries"=>"3", "net.inet.ip.rtminexpire"=>"10", "net.inet.ip.fw.dyn_buckets"=>"256", "net.inet6.ip6.accept_rtadv"=>"0", "net.inet6.ip6.rr_prune"=>"5", "net.key.ah_keymin"=>"128", "net.inet.ip.redirect"=>"1", "net.inet.tcp.sack_globalmaxholes"=>"65536", "net.inet.ip.keepfaith"=>"0", "net.inet.ip.dummynet.expire"=>"1", "net.inet.ip.gifttl"=>"30", "net.inet.ip.portrange.last"=>"65535", "net.inet.ipsec.ah_net_deflev"=>"1", "net.inet6.icmp6.nd6_delay"=>"5", "net.inet.tcp.packetchain"=>"50", "net.inet6.ip6.hdrnestlimit"=>"50", "net.inet.tcp.newreno"=>"0", "net.inet6.ip6.dad_count"=>"1", "net.inet6.ip6.auto_linklocal"=>"1", "net.inet6.ip6.temppltime"=>"86400", "net.inet.tcp.strict_rfc1948"=>"0", "net.inet.ip.dummynet.red_max_pkt_size"=>"1500", "net.inet.ip.maxfrags"=>"2048", "net.inet.tcp.log_in_vain"=>"0", "net.athdupie"=>"1 1", "net.inet.tcp.rfc1323"=>"1", "net.inet.ip.subnets_are_local"=>"0", "net.inet.ip.dummynet.search_steps"=>"0", "net.inet.icmp.icmplim"=>"250", "net.link.ether.inet.apple_hwcksum_tx"=>"1", "net.inet6.icmp6.redirtimeout"=>"600", "net.inet.ipsec.ah_cleartos"=>"1", "net.inet6.ip6.log_interval"=>"5", "net.link.ether.inet.max_age"=>"1200", "net.inet.ip.fw.enable"=>"1", "net.inet6.ip6.redirect"=>"1", "net.athaggrfmax"=>"28 28", "net.inet.ip.maxfragsperpacket"=>"128", "net.inet6.ip6.use_deprecated"=>"1", "net.link.generic.system.dlil_input_sanity_check"=>"0", "net.inet.tcp.sack_globalholes"=>"0", "net.inet.tcp.reass.cursegments"=>"0", "net.inet6.icmp6.nodeinfo"=>"3", "net.local.inflight"=>"0", "net.inet.ip.dummynet.hash_size"=>"64", "net.inet.ip.dummynet.red_avg_pkt_size"=>"512", "net.inet.ipsec.dfbit"=>"0", "net.inet.tcp.reass.overflows"=>"0", "net.inet.tcp.rexmt_thresh"=>"2", "net.inet6.ip6.maxfrags"=>"8192", "net.inet6.ip6.rtminexpire"=>"10", "net.inet6.ipsec6.esp_net_deflev"=>"1", "net.inet.tcp.blackhole"=>"0", "net.key.esp_keymin"=>"256", "net.inet.ip.check_interface"=>"0", "net.inet.tcp.minmssoverload"=>"0", "net.link.ether.inet.maxtries"=>"5", "net.inet.tcp.do_tcpdrain"=>"0", "net.inet.ipsec.esp_port"=>"4500", "net.inet6.ipsec6.ah_net_deflev"=>"1", "net.inet.ip.dummynet.extract_heap"=>"0", "net.inet.tcp.path_mtu_discovery"=>"1", "net.inet.ip.intr_queue_maxlen"=>"50", "net.inet.ipsec.def_policy"=>"1", "net.inet.ip.fw.autoinc_step"=>"100", "net.inet.ip.accept_sourceroute"=>"0", "net.inet.raw.maxdgram"=>"8192", "net.inet.ip.maxfragpackets"=>"1024", "net.inet.ip.fw.one_pass"=>"0", "net.appletalk.routermix"=>"2000", "net.inet.tcp.tcp_lq_overflow"=>"1", "net.link.generic.system.ifcount"=>"9", "net.link.ether.inet.send_conflicting_probes"=>"1", "net.inet.tcp.background_io_enabled"=>"1", "net.inet6.ipsec6.debug"=>"0", "net.inet.tcp.win_scale_factor"=>"3", "net.key.natt_keepalive_interval"=>"20", "net.inet.tcp.msl"=>"15000", "net.inet.ip.portrange.hifirst"=>"49152", "net.inet.ipsec.ah_trans_deflev"=>"1", "net.inet.tcp.rtt_min"=>"1", "net.inet6.ip6.defmcasthlim"=>"1", "net.inet6.icmp6.nd6_prune"=>"1", "net.inet6.ip6.fw.verbose"=>"0", "net.inet.ip.portrange.lowfirst"=>"1023", "net.inet.tcp.maxseg_unacked"=>"8", "net.local.dgram.maxdgram"=>"2048", "net.key.blockacq_lifetime"=>"20", "net.inet.tcp.sack_maxholes"=>"128", "net.inet6.ip6.maxfragpackets"=>"1024", "net.inet6.ip6.use_tempaddr"=>"0", "net.athpowermode"=>"0 0", "net.inet.udp.recvspace"=>"73728", "net.inet.tcp.isn_reseed_interval"=>"0", "net.inet.tcp.local_slowstart_flightsize"=>"8", "net.inet.ip.dummynet.searches"=>"0", "net.inet.ip.intr_queue_drops"=>"0", "net.link.generic.system.multi_threaded_input"=>"1", "net.inet.raw.recvspace"=>"8192", "net.inet.ipsec.esp_trans_deflev"=>"1", "net.key.prefered_oldsa"=>"0", "net.local.stream.recvspace"=>"8192", "net.inet.tcp.sockthreshold"=>"64", "net.inet6.icmp6.nd6_umaxtries"=>"3", "net.pstimeout"=>"20 20", "net.inet.ip.sourceroute"=>"0", "net.inet.ip.fw.dyn_short_lifetime"=>"5", "net.inet.tcp.minmss"=>"216", "net.inet6.ip6.gifhlim"=>"0", "net.athvendorie"=>"1 1", "net.inet.ip.check_route_selfref"=>"1", "net.inet.icmp.log_redirect"=>"0", "net.inet6.icmp6.errppslimit"=>"100", "net.inet.tcp.mssdflt"=>"512", "net.inet.icmp.drop_redirect"=>"0", "net.inet6.ipsec6.esp_randpad"=>"-1", "net.inet6.ipsec6.ah_trans_deflev"=>"1", "net.inet.ip.random_id"=>"1", "net.inet.icmp.timestamp"=>"0"}, "interfaces"=>{"stf0"=>{"flags"=>[], "number"=>"0", "mtu"=>"1280", "type"=>"stf", "encapsulation"=>"6to4"}, "vmnet1"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"1", "addresses"=>[{"broadcast"=>"192.168.88.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.88.1"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vmnet", "encapsulation"=>"Ethernet"}, "vboxnet0"=>{"flags"=>["BROADCAST", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vboxnet", "encapsulation"=>"Ethernet"}, "lo0"=>{"flags"=>["UP", "LOOPBACK", "RUNNING", "MULTICAST"], "number"=>"0", "addresses"=>[{"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6", "address"=>"fe80::1"}, {"netmask"=>"255.0.0.0", "family"=>"inet", "address"=>"127.0.0.1"}, {"scope"=>"Node", "prefixlen"=>"128", "family"=>"inet6", "address"=>"::1"}, {"scope"=>"Node", "prefixlen"=>"128", "family"=>"inet6", "address"=>"private"}], "mtu"=>"16384", "type"=>"lo", "encapsulation"=>"Loopback"}, "vboxn"=>{"counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}}, "gif0"=>{"flags"=>["POINTOPOINT", "MULTICAST"], "number"=>"0", "mtu"=>"1280", "type"=>"gif", "encapsulation"=>"IPIP"}, "vmnet"=>{"counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}}, "en0"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"inactive", "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"en", "media"=>{"supported"=>[{"autoselect"=>{"options"=>[]}}, {"10baseT/UTP"=>{"options"=>["half-duplex"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex", "flow-control"]}}, {"100baseTX"=>{"options"=>["half-duplex"]}}, {"100baseTX"=>{"options"=>["full-duplex"]}}, {"100baseTX"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"100baseTX"=>{"options"=>["full-duplex", "flow-control"]}}, {"1000baseT"=>{"options"=>["full-duplex"]}}, {"1000baseT"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"1000baseT"=>{"options"=>["full-duplex", "flow-control"]}}, {"none"=>{"options"=>[]}}], "selected"=>[{"autoselect"=>{"options"=>[]}}]}, "counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"342", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"Ethernet"}, "vmnet8"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"8", "addresses"=>[{"broadcast"=>"192.168.237.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.237.1"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vmnet", "encapsulation"=>"Ethernet"}, "en1"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"active", "number"=>"1", "addresses"=>[{"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6", "address"=>"private"}, {"broadcast"=>"192.168.1.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.1.4"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"en", "media"=>{"supported"=>[{"autoselect"=>{"options"=>[]}}], "selected"=>[{"autoselect"=>{"options"=>[]}}]}, "counters"=>{"tx"=>{"packets"=>"7041789", "bytes"=>"449206298", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"95", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"19966002", "bytes"=>"13673879120", "compressed"=>0, "errors"=>"1655893", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"Ethernet", "arp"=>{"192.168.1.7"=>"private"}}, "fw0"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"inactive", "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"4078", "type"=>"fw", "media"=>{"supported"=>[{"autoselect"=>{"options"=>["full-duplex"]}}], "selected"=>[{"autoselect"=>{"options"=>["full-duplex"]}}]}, "counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"346", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"1394"}}}, "os"=>"darwin", "domain"=>"local", "ohai_time"=>1240624355.08575, "platform_build"=>"9G55", "os_version"=>"9.6.0", "hostname"=>"local", "languages"=>{"ruby"=>{"target_os"=>"darwin9.0", "platform"=>"universal-darwin9.0", "host_vendor"=>"apple", "target_cpu"=>"i686", "target_vendor"=>"apple", "host_os"=>"darwin9.0", "version"=>"1.8.6", "host_cpu"=>"i686", "host"=>"i686-apple-darwin9.0", "release_date"=>"2008-03-03", "target"=>"i686-apple-darwin9.0"}}, "macaddress"=>"private"}
diff --git a/benchmarks/parser2_benchmark.rb b/benchmarks/parser2_benchmark.rb
new file mode 100755
index 0000000..bc80772
--- /dev/null
+++ b/benchmarks/parser2_benchmark.rb
@@ -0,0 +1,237 @@
+#!/usr/bin/env ruby
+# CODING: UTF-8
+
+require 'rbconfig'
+RUBY_PATH=File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+RAKE_PATH=File.join(Config::CONFIG['bindir'], 'rake')
+require 'bullshit'
+case ARGV.first
+when 'ext'
+ require 'json/ext'
+when 'pure'
+ require 'json/pure'
+when 'yaml'
+ require 'yaml'
+ require 'json/pure'
+when 'rails'
+ require 'active_support'
+ require 'json/pure'
+when 'yajl'
+ require 'yajl'
+ require 'json/pure'
+else
+ require 'json/pure'
+end
+
+module Parser2BenchmarkCommon
+ include JSON
+
+ def setup
+ @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json'))
+ end
+
+ def generic_reset_method
+ @result == @big or raise "not equal"
+ end
+end
+
+class Parser2BenchmarkExt < Bullshit::RepeatCase
+ include Parser2BenchmarkCommon
+
+ warmup yes
+ iterations 4000
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def benchmark_parser
+ @result = JSON.parse(@json)
+ end
+
+ alias reset_parser generic_reset_method
+end
+
+class Parser2BenchmarkPure < Bullshit::RepeatCase
+ include Parser2BenchmarkCommon
+
+ warmup yes
+ iterations 500
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def benchmark_parser
+ @result = JSON.parse(@json)
+ end
+
+ alias reset_parser generic_reset_method
+end
+
+class Parser2BenchmarkYAML < Bullshit::RepeatCase
+ warmup yes
+ iterations 1000
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def setup
+ @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json'))
+ end
+
+ def benchmark_parser
+ @result = YAML.load(@json)
+ end
+
+ def generic_reset_method
+ @result == @big or raise "not equal"
+ end
+end
+
+class Parser2BenchmarkRails < Bullshit::RepeatCase
+ warmup yes
+ iterations 500
+
+ truncate_data do
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def setup
+ a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ]
+ @big = a * 100
+ @json = JSON.generate(@big)
+ end
+
+ def benchmark_parser
+ @result = ActiveSupport::JSON.decode(@json)
+ end
+
+ def generic_reset_method
+ @result == @big or raise "not equal"
+ end
+end
+
+class Parser2BenchmarkYajl < Bullshit::RepeatCase
+ warmup yes
+ iterations 4000
+
+ truncate_data do
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def setup
+ @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json'))
+ end
+
+ def benchmark_parser
+ @result = Yajl::Parser.new.parse(@json)
+ end
+
+ def generic_reset_method
+ @result == @big or raise "not equal"
+ end
+end
+
+if $0 == __FILE__
+ Bullshit::Case.autorun false
+
+ case ARGV.first
+ when 'ext'
+ Parser2BenchmarkExt.run
+ when 'pure'
+ Parser2BenchmarkPure.run
+ when 'yaml'
+ Parser2BenchmarkYAML.run
+ when 'rails'
+ Parser2BenchmarkRails.run
+ when 'yajl'
+ Parser2BenchmarkYajl.run
+ else
+ system "#{RAKE_PATH} clean"
+ system "#{RUBY_PATH} #$0 yaml"
+ system "#{RUBY_PATH} #$0 rails"
+ system "#{RUBY_PATH} #$0 pure"
+ system "#{RAKE_PATH} compile_ext"
+ system "#{RUBY_PATH} #$0 ext"
+ system "#{RUBY_PATH} #$0 yajl"
+ Bullshit.compare do
+ output_filename File.join(File.dirname(__FILE__), 'data', 'Parser2BenchmarkComparison.log')
+
+ benchmark Parser2BenchmarkExt, :parser, :load => yes
+ benchmark Parser2BenchmarkPure, :parser, :load => yes
+ benchmark Parser2BenchmarkYAML, :parser, :load => yes
+ benchmark Parser2BenchmarkRails, :parser, :load => yes
+ benchmark Parser2BenchmarkYajl, :parser, :load => yes
+ end
+ end
+end
diff --git a/benchmarks/parser_benchmark.rb b/benchmarks/parser_benchmark.rb
index 38af54d..acb8ea4 100755
--- a/benchmarks/parser_benchmark.rb
+++ b/benchmarks/parser_benchmark.rb
@@ -16,6 +16,9 @@ when 'yaml'
when 'rails'
require 'active_support'
require 'json/pure'
+when 'yajl'
+ require 'yajl'
+ require 'json/pure'
else
require 'json/pure'
end
@@ -38,9 +41,10 @@ class ParserBenchmarkExt < Bullshit::RepeatCase
include ParserBenchmarkCommon
warmup yes
- iterations 1000
+ iterations 8000
truncate_data do
+ enabled false
alpha_level 0.05
window_size 50
slope_angle 0.1
@@ -68,9 +72,10 @@ class ParserBenchmarkPure < Bullshit::RepeatCase
include ParserBenchmarkCommon
warmup yes
- iterations 1000
+ iterations 500
truncate_data do
+ enabled false
alpha_level 0.05
window_size 50
slope_angle 0.1
@@ -99,6 +104,7 @@ class ParserBenchmarkYAML < Bullshit::RepeatCase
iterations 1000
truncate_data do
+ enabled false
alpha_level 0.05
window_size 50
slope_angle 0.1
@@ -132,9 +138,10 @@ end
class ParserBenchmarkRails < Bullshit::RepeatCase
warmup yes
- iterations 1000
+ iterations 500
truncate_data do
+ enabled false
alpha_level 0.05
window_size 50
slope_angle 0.1
@@ -166,6 +173,43 @@ class ParserBenchmarkRails < Bullshit::RepeatCase
end
end
+class ParserBenchmarkYajl < Bullshit::RepeatCase
+ warmup yes
+ iterations 8000
+
+ truncate_data do
+ enabled false
+ alpha_level 0.05
+ window_size 50
+ slope_angle 0.1
+ end
+
+ autocorrelation do
+ alpha_level 0.05
+ max_lags 50
+ file yes
+ end
+
+ output_dir File.join(File.dirname(__FILE__), 'data')
+ output_filename benchmark_name + '.log'
+ data_file yes
+ histogram yes
+
+ def setup
+ a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ]
+ @big = a * 100
+ @json = JSON.generate(@big)
+ end
+
+ def benchmark_parser
+ @result = Yajl::Parser.new.parse(@json)
+ end
+
+ def generic_reset_method
+ @result == @big or raise "not equal"
+ end
+end
+
if $0 == __FILE__
Bullshit::Case.autorun false
@@ -178,6 +222,8 @@ if $0 == __FILE__
ParserBenchmarkYAML.run
when 'rails'
ParserBenchmarkRails.run
+ when 'yajl'
+ ParserBenchmarkYajl.run
else
system "#{RAKE_PATH} clean"
system "#{RUBY_PATH} #$0 yaml"
@@ -185,6 +231,7 @@ if $0 == __FILE__
system "#{RUBY_PATH} #$0 pure"
system "#{RAKE_PATH} compile_ext"
system "#{RUBY_PATH} #$0 ext"
+ system "#{RUBY_PATH} #$0 yajl"
Bullshit.compare do
output_filename File.join(File.dirname(__FILE__), 'data', 'ParserBenchmarkComparison.log')
@@ -192,6 +239,7 @@ if $0 == __FILE__
benchmark ParserBenchmarkPure, :parser, :load => yes
benchmark ParserBenchmarkYAML, :parser, :load => yes
benchmark ParserBenchmarkRails, :parser, :load => yes
+ benchmark ParserBenchmarkYajl, :parser, :load => yes
end
end
end
diff --git a/ext/json/ext/extconf_generator.rb b/ext/json/ext/extconf_generator.rb
new file mode 100644
index 0000000..33a5625
--- /dev/null
+++ b/ext/json/ext/extconf_generator.rb
@@ -0,0 +1,18 @@
+require 'mkmf'
+require 'rbconfig'
+
+unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O3')
+ $CFLAGS << ' -O3'
+end
+if CONFIG['CC'] =~ /gcc/
+ $CFLAGS << ' -Wall'
+ #$CFLAGS.gsub!(/ -O[\dsz]?/, ' -O0 -ggdb')
+end
+if RUBY_VERSION >= '1.9'
+ $CFLAGS << ' -DRUBY_19'
+end
+
+have_header("ruby/st.h") || have_header("st.h")
+have_header("ruby/re.h") || have_header("re.h")
+have_header("ruby/encoding.h")
+create_makefile 'generator'
diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/extconf_parser.rb
index 6226394..9662e9a 100644
--- a/ext/json/ext/parser/extconf.rb
+++ b/ext/json/ext/extconf_parser.rb
@@ -1,9 +1,12 @@
require 'mkmf'
require 'rbconfig'
+unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O3')
+ $CFLAGS << ' -O3'
+end
if CONFIG['CC'] =~ /gcc/
- $CFLAGS += ' -Wall'
- #$CFLAGS += ' -O0 -ggdb'
+ $CFLAGS << ' -Wall'
+ #$CFLAGS.gsub!(/ -O[\dsz]?/, ' -O0 -ggdb')
end
have_header("ruby/st.h") || have_header("st.h")
diff --git a/ext/json/ext/generator.c b/ext/json/ext/generator.c
new file mode 100644
index 0000000..1db22a7
--- /dev/null
+++ b/ext/json/ext/generator.c
@@ -0,0 +1,1256 @@
+#include "generator.h"
+
+#ifdef HAVE_RUBY_ENCODING_H
+static VALUE CEncoding_UTF_8;
+static ID i_encoding, i_encode;
+#endif
+
+static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
+ mHash, mArray, mInteger, mFloat, mString, mString_Extend,
+ mTrueClass, mFalseClass, mNilClass, eGeneratorError,
+ eNestingError, CRegexp_MULTILINE;
+
+static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
+ i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
+ i_pack, i_unpack, i_create_id, i_extend;
+
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns 0. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+static unsigned char isLegalUTF8(const UTF8 *source, int length)
+{
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return 0;
+ /* Everything else falls through when "1"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return 0; break;
+ case 0xED: if (a > 0x9F) return 0; break;
+ case 0xF0: if (a < 0x90) return 0; break;
+ case 0xF4: if (a > 0x8F) return 0; break;
+ default: if (a < 0x80) return 0;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
+ }
+ if (*source > 0xF4) return 0;
+ return 1;
+}
+
+/* Escapes the UTF16 character and stores the result in the buffer buf. */
+static void unicode_escape(char *buf, UTF16 character)
+{
+ const char *digits = "0123456789abcdef";
+
+ buf[2] = digits[character >> 12];
+ buf[3] = digits[(character >> 8) & 0xf];
+ buf[4] = digits[(character >> 4) & 0xf];
+ buf[5] = digits[character & 0xf];
+}
+
+/* Escapes the UTF16 character and stores the result in the buffer buf, then
+ * the buffer buf іs appended to the FBuffer buffer. */
+static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
+ character)
+{
+ unicode_escape(buf, character);
+ fbuffer_append(buffer, buf, 6);
+}
+
+/* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
+ * and control characters are JSON escaped. */
+static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
+{
+ const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
+ const UTF8 *sourceEnd = source + RSTRING_LEN(string);
+ char buf[6] = { '\\', 'u' };
+
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "partial character in source, but hit end");
+ }
+ if (!isLegalUTF8(source, extraBytesToRead+1)) {
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "source sequence is illegal/malformed utf-8");
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+#if UNI_STRICT_CONVERSION
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "source sequence is illegal/malformed utf-8");
+#else
+ unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
+#endif
+ } else {
+ /* normal case */
+ if (ch >= 0x20 && ch <= 0x7f) {
+ switch (ch) {
+ case '\\':
+ fbuffer_append(buffer, "\\\\", 2);
+ break;
+ case '"':
+ fbuffer_append(buffer, "\\\"", 2);
+ break;
+ default:
+ fbuffer_append_char(buffer, ch);
+ break;
+ }
+ } else {
+ switch (ch) {
+ case '\n':
+ fbuffer_append(buffer, "\\n", 2);
+ break;
+ case '\r':
+ fbuffer_append(buffer, "\\r", 2);
+ break;
+ case '\t':
+ fbuffer_append(buffer, "\\t", 2);
+ break;
+ case '\f':
+ fbuffer_append(buffer, "\\f", 2);
+ break;
+ case '\b':
+ fbuffer_append(buffer, "\\b", 2);
+ break;
+ default:
+ unicode_escape_to_buffer(buffer, buf, (UTF16) ch);
+ break;
+ }
+ }
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+#if UNI_STRICT_CONVERSION
+ source -= (extraBytesToRead+1); /* return to the start */
+ rb_raise(rb_path2class("JSON::GeneratorError"),
+ "source sequence is illegal/malformed utf8");
+#else
+ unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
+#endif
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ ch -= halfBase;
+ unicode_escape_to_buffer(buffer, buf, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START));
+ unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
+ }
+ }
+}
+
+/* Converts string to a JSON string in FBuffer buffer, where only the
+ * characters required by the JSON standard are JSON escaped. The remaining
+ * characters (should be UTF8) are just passed through and appended to the
+ * result. */
+static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
+{
+ const char *ptr = RSTRING_PTR(string), *p;
+ int len = RSTRING_LEN(string), start = 0, end = 0;
+ const char *escape = NULL;
+ int escape_len;
+ unsigned char c;
+ char buf[6] = { '\\', 'u' };
+
+ for (start = 0, end = 0; end < len;) {
+ p = ptr + end;
+ c = (unsigned char) *p;
+ if (c < 0x20) {
+ switch (c) {
+ case '\n':
+ escape = "\\n";
+ escape_len = 2;
+ break;
+ case '\r':
+ escape = "\\r";
+ escape_len = 2;
+ break;
+ case '\t':
+ escape = "\\t";
+ escape_len = 2;
+ break;
+ case '\f':
+ escape = "\\f";
+ escape_len = 2;
+ break;
+ case '\b':
+ escape = "\\b";
+ escape_len = 2;
+ break;
+ default:
+ unicode_escape(buf, (UTF16) *p);
+ escape = buf;
+ escape_len = 6;
+ break;
+ }
+ } else {
+ switch (c) {
+ case '\\':
+ escape = "\\\\";
+ escape_len = 2;
+ break;
+ case '"':
+ escape = "\\\"";
+ escape_len = 2;
+ break;
+ default:
+ end++;
+ continue;
+ break;
+ }
+ }
+ fbuffer_append(buffer, ptr + start, end - start);
+ fbuffer_append(buffer, escape, escape_len);
+ start = ++end;
+ escape = NULL;
+ }
+ fbuffer_append(buffer, ptr + start, end - start);
+}
+
+/* fbuffer implementation */
+
+static FBuffer *fbuffer_alloc()
+{
+ FBuffer *fb = ALLOC(FBuffer);
+ memset((void *) fb, 0, sizeof(FBuffer));
+ fb->initial_length = FBUFFER_INITIAL_LENGTH;
+ return fb;
+}
+
+static FBuffer *fbuffer_alloc_with_length(unsigned int initial_length)
+{
+ assert(initial_length > 0);
+ FBuffer *fb = ALLOC(FBuffer);
+ memset((void *) fb, 0, sizeof(FBuffer));
+ fb->initial_length = initial_length;
+ return fb;
+}
+
+
+static void fbuffer_free(FBuffer *fb)
+{
+ if (fb->ptr) ruby_xfree(fb->ptr);
+ ruby_xfree(fb);
+}
+
+static void fbuffer_free_only_buffer(FBuffer *fb)
+{
+ ruby_xfree(fb);
+}
+
+static void fbuffer_clear(FBuffer *fb)
+{
+ fb->len = 0;
+}
+
+static void fbuffer_inc_capa(FBuffer *fb, unsigned int requested)
+{
+ unsigned int required;
+
+ if (!fb->ptr) {
+ fb->ptr = ALLOC_N(char, fb->initial_length);
+ fb->capa = fb->initial_length;
+ }
+
+ for (required = fb->capa; requested > required - fb->len; required <<= 1);
+
+ if (required > fb->capa) {
+ fb->ptr = (char *) REALLOC_N((long*) fb->ptr, char, required);
+ fb->capa = required;
+ }
+}
+
+static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned int len)
+{
+ if (len > 0) {
+ fbuffer_inc_capa(fb, len);
+ memcpy(fb->ptr + fb->len, newstr, len);
+ fb->len += len;
+ }
+}
+
+static void fbuffer_append_char(FBuffer *fb, char newchr)
+{
+ fbuffer_inc_capa(fb, 1);
+ *(fb->ptr + fb->len) = newchr;
+ fb->len++;
+}
+
+/*
+ * Document-module: JSON::Ext::Generator
+ *
+ * This is the JSON generator implemented as a C extension. It can be
+ * configured to be used by setting
+ *
+ * JSON.generator = JSON::Ext::Generator
+ *
+ * with the method generator= in JSON.
+ *
+ */
+
+/*
+ * call-seq: to_json(state = nil, depth = 0)
+ *
+ * Returns a JSON string containing a JSON object, that is generated from
+ * this Hash instance.
+ * _state_ is a JSON::State object, that can also be used to configure the
+ * produced JSON string output further.
+ * _depth_ is used to find out nesting depth, to indent accordingly.
+ */
+static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: to_json(state = nil, depth = 0)
+ *
+ * Returns a JSON string containing a JSON array, that is generated from
+ * this Array instance.
+ * _state_ is a JSON::State object, that can also be used to configure the
+ * produced JSON string output further.
+ * _depth_ is used to find out nesting depth, to indent accordingly.
+ */
+static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
+ VALUE state, depth;
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string representation for this Integer number.
+ */
+static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string representation for this Float number.
+ */
+static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: String.included(modul)
+ *
+ * Extends _modul_ with the String::Extend module.
+ */
+static VALUE mString_included_s(VALUE self, VALUE modul) {
+ VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
+ return result;
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * This string should be encoded with UTF-8 A call to this method
+ * returns a JSON string encoded with UTF16 big endian characters as
+ * \u????.
+ */
+static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: to_json_raw_object()
+ *
+ * This method creates a raw object hash, that can be nested into
+ * other data structures and will be generated as a raw string. This
+ * method should be used, if you want to convert raw strings to JSON
+ * instead of UTF-8 strings, e. g. binary data.
+ */
+static VALUE mString_to_json_raw_object(VALUE self)
+{
+ VALUE ary;
+ VALUE result = rb_hash_new();
+ rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
+ ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
+ rb_hash_aset(result, rb_str_new2("raw"), ary);
+ return result;
+}
+
+/*
+ * call-seq: to_json_raw(*args)
+ *
+ * This method creates a JSON text from the result of a call to
+ * to_json_raw_object of this String.
+ */
+static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
+{
+ VALUE obj = mString_to_json_raw_object(self);
+ Check_Type(obj, T_HASH);
+ return mHash_to_json(argc, argv, obj);
+}
+
+/*
+ * call-seq: json_create(o)
+ *
+ * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
+ * key "raw"). The Ruby String can be created by this module method.
+ */
+static VALUE mString_Extend_json_create(VALUE self, VALUE o)
+{
+ VALUE ary;
+ Check_Type(o, T_HASH);
+ ary = rb_hash_aref(o, rb_str_new2("raw"));
+ return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string for true: 'true'.
+ */
+static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Returns a JSON string for false: 'false'.
+ */
+static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ */
+static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, self, depth);
+}
+
+/*
+ * call-seq: to_json(*)
+ *
+ * Converts this object to a string (calling #to_s), converts
+ * it to a JSON string, and returns the result. This is a fallback, if no
+ * special method #to_json was defined for some object.
+ */
+static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
+{
+ VALUE state, depth;
+ VALUE string = rb_funcall(self, i_to_s, 0);
+ rb_scan_args(argc, argv, "02", &state, &depth);
+ Check_Type(string, T_STRING);
+ state = cState_from_state_s(cState, state);
+ return cState_partial_generate(state, string, depth);
+}
+
+static void State_free(JSON_Generator_State *state)
+{
+ if (state->indent) ruby_xfree(state->indent);
+ if (state->space) ruby_xfree(state->space);
+ if (state->space_before) ruby_xfree(state->space_before);
+ if (state->object_nl) ruby_xfree(state->object_nl);
+ if (state->array_nl) ruby_xfree(state->array_nl);
+ if (state->array_delim) fbuffer_free(state->array_delim);
+ if (state->object_delim) fbuffer_free(state->object_delim);
+ if (state->object_delim2) fbuffer_free(state->object_delim2);
+ ruby_xfree(state);
+}
+
+static JSON_Generator_State *State_allocate()
+{
+ JSON_Generator_State *state = ALLOC(JSON_Generator_State);
+ return state;
+}
+
+static VALUE cState_s_allocate(VALUE klass)
+{
+ JSON_Generator_State *state = State_allocate();
+ return Data_Wrap_Struct(klass, NULL, State_free, state);
+}
+
+/*
+ * call-seq: configure(opts)
+ *
+ * Configure this State instance with the Hash _opts_, and return
+ * itself.
+ */
+static VALUE cState_configure(VALUE self, VALUE opts)
+{
+ VALUE tmp;
+ GET_STATE(self);
+ tmp = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
+ if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
+ if (NIL_P(tmp)) {
+ rb_raise(rb_eArgError, "opts has to be hash like or convertable into a hash");
+ }
+ opts = tmp;
+ tmp = rb_hash_aref(opts, ID2SYM(i_indent));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->indent = strdup(RSTRING_PTR(tmp));
+ state->indent_len = strlen(state->indent);
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_space));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->space = strdup(RSTRING_PTR(tmp));
+ state->space_len = strlen(state->space);
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->space_before = strdup(RSTRING_PTR(tmp));
+ state->space_before_len = strlen(state->space_before);
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->array_nl = strdup(RSTRING_PTR(tmp));
+ state->array_nl_len = strlen(state->array_nl);
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
+ if (RTEST(tmp)) {
+ Check_Type(tmp, T_STRING);
+ state->object_nl = strdup(RSTRING_PTR(tmp));
+ state->object_nl_len = strlen(state->object_nl);
+ }
+ tmp = ID2SYM(i_max_nesting);
+ state->max_nesting = 19;
+ if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
+ if (RTEST(max_nesting)) {
+ Check_Type(max_nesting, T_FIXNUM);
+ state->max_nesting = FIX2LONG(max_nesting);
+ } else {
+ state->max_nesting = 0;
+ }
+ }
+ tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
+ state->allow_nan = RTEST(tmp);
+ tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
+ state->ascii_only = RTEST(tmp);
+ return self;
+}
+
+/*
+ * call-seq: to_h
+ *
+ * Returns the configuration instance variables as a hash, that can be
+ * passed to the configure method.
+ */
+static VALUE cState_to_h(VALUE self)
+{
+ VALUE result = rb_hash_new();
+ GET_STATE(self);
+ rb_hash_aset(result, ID2SYM(i_indent), rb_str_new2(state->indent));
+ rb_hash_aset(result, ID2SYM(i_space), rb_str_new2(state->space));
+ rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new2(state->space_before));
+ rb_hash_aset(result, ID2SYM(i_object_nl), rb_str_new2(state->object_nl));
+ rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new2(state->array_nl));
+ rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
+ rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
+ return result;
+}
+
+/*
+ * The fbuffer2rstring breaks encapsulation of Ruby's String datatype to avoid
+ * calling memcpy while creating a RString from a c string. This is rather
+ * hackish code, I am not sure if it's a good idea to keep it.
+ */
+#ifdef RUBY_19
+#define STR_NOEMBED FL_USER1
+
+#define STR_SET_EMBED_LEN(str, n) do { \
+ long tmp_n = (n);\
+ RBASIC(str)->flags &= ~RSTRING_EMBED_LEN_MASK;\
+ RBASIC(str)->flags |= (tmp_n) << RSTRING_EMBED_LEN_SHIFT;\
+} while (0)
+
+#define STR_SET_NOEMBED(str) do {\
+ FL_SET(str, STR_NOEMBED);\
+ STR_SET_EMBED_LEN(str, 0);\
+} while (0)
+
+static VALUE fbuffer2rstring(FBuffer *buffer)
+{
+ NEWOBJ(str, struct RString);
+ OBJSETUP(str, rb_cString, T_STRING);
+
+ str->as.heap.ptr = FBUFFER_PTR(buffer);
+ str->as.heap.len = FBUFFER_LEN(buffer);
+ str->as.heap.aux.capa = FBUFFER_CAPA(buffer);
+ STR_SET_NOEMBED(str);
+
+ return (VALUE) str;
+}
+#else
+static VALUE fbuffer2rstring(FBuffer *buffer)
+{
+ NEWOBJ(str, struct RString);
+ OBJSETUP(str, rb_cString, T_STRING);
+
+ str->ptr = FBUFFER_PTR(buffer);
+ str->len = FBUFFER_LEN(buffer);
+ str->aux.capa = FBUFFER_CAPA(buffer);
+
+ return (VALUE) str;
+}
+#endif
+
+static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth)
+{
+ VALUE tmp;
+ switch (TYPE(obj)) {
+ case T_HASH:
+ {
+ char *object_nl = state->object_nl;
+ long object_nl_len = state->object_nl_len;
+ char *indent = state->indent;
+ long indent_len = state->indent_len;
+ long max_nesting = state->max_nesting;
+ char *delim = FBUFFER_PTR(state->object_delim);
+ long delim_len = FBUFFER_LEN(state->object_delim);
+ char *delim2 = FBUFFER_PTR(state->object_delim2);
+ long delim2_len = FBUFFER_LEN(state->object_delim2);
+ int i, j;
+ depth++;
+ if (max_nesting != 0 && depth > max_nesting) {
+ fbuffer_free(buffer);
+ rb_raise(eNestingError, "nesting of %ld is too deep", depth);
+ }
+ fbuffer_append_char(buffer, '{');
+ VALUE keys = rb_funcall(obj, rb_intern("keys"), 0);
+ VALUE key, key_to_s;
+ for(i = 0; i < RARRAY_LEN(keys); i++) {
+ if (i > 0) fbuffer_append(buffer, delim, delim_len);
+ if (object_nl) {
+ fbuffer_append(buffer, object_nl, object_nl_len);
+ }
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+ key = rb_ary_entry(keys, i);
+ key_to_s = rb_funcall(key, i_to_s, 0);
+ Check_Type(key_to_s, T_STRING);
+ generate_json(buffer, Vstate, state, key_to_s, depth);
+ fbuffer_append(buffer, delim2, delim2_len);
+ generate_json(buffer, Vstate, state, rb_hash_aref(obj, key), depth);
+ }
+ depth--;
+ if (object_nl) {
+ fbuffer_append(buffer, object_nl, object_nl_len);
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+ }
+ fbuffer_append_char(buffer, '}');
+ }
+ break;
+ case T_ARRAY:
+ {
+ char *array_nl = state->array_nl;
+ long array_nl_len = state->array_nl_len;
+ char *indent = state->indent;
+ long indent_len = state->indent_len;
+ long max_nesting = state->max_nesting;
+ char *delim = FBUFFER_PTR(state->array_delim);
+ long delim_len = FBUFFER_LEN(state->array_delim);
+ int i, j;
+ depth++;
+ if (max_nesting != 0 && depth > max_nesting) {
+ fbuffer_free(buffer);
+ rb_raise(eNestingError, "nesting of %ld is too deep", depth);
+ }
+ fbuffer_append_char(buffer, '[');
+ if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len);
+ for(i = 0; i < RARRAY_LEN(obj); i++) {
+ if (i > 0) fbuffer_append(buffer, delim, delim_len);
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+ generate_json(buffer, Vstate, state, rb_ary_entry(obj, i), depth);
+ }
+ depth--;
+ if (array_nl) {
+ fbuffer_append(buffer, array_nl, array_nl_len);
+ if (indent) {
+ for (j = 0; j < depth; j++) {
+ fbuffer_append(buffer, indent, indent_len);
+ }
+ }
+ }
+ fbuffer_append_char(buffer, ']');
+ }
+ break;
+ case T_STRING:
+ fbuffer_append_char(buffer, '"');
+#ifdef HAVE_RUBY_ENCODING_H
+ obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8);
+#endif
+ if (state->ascii_only) {
+ convert_UTF8_to_JSON_ASCII(buffer, obj);
+ } else {
+ convert_UTF8_to_JSON(buffer, obj);
+ }
+ fbuffer_append_char(buffer, '"');
+ break;
+ case T_NIL:
+ fbuffer_append(buffer, "null", 4);
+ break;
+ case T_FALSE:
+ fbuffer_append(buffer, "false", 5);
+ break;
+ case T_TRUE:
+ fbuffer_append(buffer, "true", 4);
+ break;
+ case T_FIXNUM:
+ case T_BIGNUM:
+ tmp = rb_funcall(obj, i_to_s, 0);
+ fbuffer_append(buffer, RSTRING_PAIR(tmp));
+ break;
+ case T_FLOAT:
+ {
+ char allow_nan = state->allow_nan;
+ double value = RFLOAT_VALUE(obj);
+ tmp = rb_funcall(obj, i_to_s, 0);
+ if (!allow_nan) {
+ if (isinf(value)) {
+ fbuffer_free(buffer);
+ rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
+ } else if (isnan(value)) {
+ fbuffer_free(buffer);
+ rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
+ }
+ }
+ fbuffer_append(buffer, RSTRING_PAIR(tmp));
+ }
+ break;
+ default:
+ if (rb_respond_to(obj, i_to_json)) {
+ tmp = rb_funcall(obj, i_to_json, 2, Vstate, INT2FIX(depth + 1));
+ Check_Type(tmp, T_STRING);
+ fbuffer_append(buffer, RSTRING_PAIR(tmp));
+ } else {
+ tmp = rb_funcall(obj, i_to_s, 0);
+ Check_Type(tmp, T_STRING);
+ generate_json(buffer, Vstate, state, tmp, depth + 1);
+ }
+ break;
+ }
+}
+
+/*
+ * call-seq: partial_generate(obj)
+ *
+ * Generates a part of a JSON document from object +obj+ and returns the
+ * result.
+ */
+static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth)
+{
+ VALUE result;
+ FBuffer *buffer = fbuffer_alloc();
+ GET_STATE(self);
+
+ if (state->object_delim) {
+ fbuffer_clear(state->object_delim);
+ } else {
+ state->object_delim = fbuffer_alloc_with_length(16);
+ }
+ fbuffer_append_char(state->object_delim, ',');
+ if (state->object_delim2) {
+ fbuffer_clear(state->object_delim2);
+ } else {
+ state->object_delim2 = fbuffer_alloc_with_length(16);
+ }
+ fbuffer_append_char(state->object_delim2, ':');
+ if (state->space) fbuffer_append(state->object_delim2, state->space, state->space_len);
+
+ if (state->array_delim) {
+ fbuffer_clear(state->array_delim);
+ } else {
+ state->array_delim = fbuffer_alloc_with_length(16);
+ }
+ fbuffer_append_char(state->array_delim, ',');
+ if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len);
+
+ generate_json(buffer, self, state, obj, NIL_P(depth) ? 0 : FIX2INT(depth));
+ result = fbuffer2rstring(buffer);
+ fbuffer_free_only_buffer(buffer);
+ FORCE_UTF8(result);
+ return result;
+}
+
+/*
+ * call-seq: generate(obj)
+ *
+ * Generates a valid JSON document from object +obj+ and returns the
+ * result. If no valid JSON document can be created this method raises a
+ * GeneratorError exception.
+ */
+static VALUE cState_generate(VALUE self, VALUE obj)
+{
+ VALUE result = cState_partial_generate(self, obj, Qnil);
+ VALUE re, args[2];
+ args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z");
+ args[1] = CRegexp_MULTILINE;
+ re = rb_class_new_instance(2, args, rb_cRegexp);
+ if (NIL_P(rb_reg_match(re, result))) {
+ rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed");
+ }
+ return result;
+}
+
+/*
+ * call-seq: new(opts = {})
+ *
+ * Instantiates a new State object, configured by _opts_.
+ *
+ * _opts_ can have the following keys:
+ *
+ * * *indent*: a string used to indent levels (default: ''),
+ * * *space*: a string that is put after, a : or , delimiter (default: ''),
+ * * *space_before*: a string that is put before a : pair delimiter (default: ''),
+ * * *object_nl*: a string that is put at the end of a JSON object (default: ''),
+ * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
+ * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
+ * generated, otherwise an exception is thrown, if these values are
+ * encountered. This options defaults to false.
+ */
+static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE opts;
+ GET_STATE(self);
+ MEMZERO(state, JSON_Generator_State, 1);
+ state->max_nesting = 19;
+ rb_scan_args(argc, argv, "01", &opts);
+ if (!NIL_P(opts)) cState_configure(self, opts);
+ return self;
+}
+
+/*
+ * call-seq: from_state(opts)
+ *
+ * Creates a State object from _opts_, which ought to be Hash to create a
+ * new State instance configured by _opts_, something else to create an
+ * unconfigured instance. If _opts_ is a State object, it is just returned.
+ */
+static VALUE cState_from_state_s(VALUE self, VALUE opts)
+{
+ if (rb_obj_is_kind_of(opts, self)) {
+ return opts;
+ } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
+ return rb_funcall(self, i_new, 1, opts);
+ } else {
+ return rb_funcall(self, i_new, 0);
+ }
+}
+
+/*
+ * call-seq: indent()
+ *
+ * This string is used to indent levels in the JSON text.
+ */
+static VALUE cState_indent(VALUE self)
+{
+ GET_STATE(self);
+ return state->indent ? rb_str_new2(state->indent) : rb_str_new2("");
+}
+
+/*
+ * call-seq: indent=(indent)
+ *
+ * This string is used to indent levels in the JSON text.
+ */
+static VALUE cState_indent_set(VALUE self, VALUE indent)
+{
+ GET_STATE(self);
+ Check_Type(indent, T_STRING);
+ if (RSTRING_LEN(indent) == 0) {
+ if (state->indent) {
+ ruby_xfree(state->indent);
+ state->indent = NULL;
+ }
+ } else {
+ if (state->indent) ruby_xfree(state->indent);
+ state->indent = strdup(RSTRING_PTR(indent));
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: space()
+ *
+ * This string is used to insert a space between the tokens in a JSON
+ * string.
+ */
+static VALUE cState_space(VALUE self)
+{
+ GET_STATE(self);
+ return state->space ? rb_str_new2(state->space) : rb_str_new2("");
+}
+
+/*
+ * call-seq: space=(space)
+ *
+ * This string is used to insert a space between the tokens in a JSON
+ * string.
+ */
+static VALUE cState_space_set(VALUE self, VALUE space)
+{
+ GET_STATE(self);
+ Check_Type(space, T_STRING);
+ if (RSTRING_LEN(space) == 0) {
+ if (state->space) {
+ ruby_xfree(state->space);
+ state->space = NULL;
+ }
+ } else {
+ if (state->space) ruby_xfree(state->space);
+ state->space = strdup(RSTRING_PTR(space));
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: space_before()
+ *
+ * This string is used to insert a space before the ':' in JSON objects.
+ */
+static VALUE cState_space_before(VALUE self)
+{
+ GET_STATE(self);
+ return state->space_before ? rb_str_new2(state->space_before) : rb_str_new2("");
+}
+
+/*
+ * call-seq: space_before=(space_before)
+ *
+ * This string is used to insert a space before the ':' in JSON objects.
+ */
+static VALUE cState_space_before_set(VALUE self, VALUE space_before)
+{
+ GET_STATE(self);
+ Check_Type(space_before, T_STRING);
+ if (RSTRING_LEN(space_before) == 0) {
+ if (state->space_before) {
+ ruby_xfree(state->space_before);
+ state->space_before = NULL;
+ }
+ } else {
+ if (state->space_before) ruby_xfree(state->space_before);
+ state->space_before = strdup(RSTRING_PTR(space_before));
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: object_nl()
+ *
+ * This string is put at the end of a line that holds a JSON object (or
+ * Hash).
+ */
+static VALUE cState_object_nl(VALUE self)
+{
+ GET_STATE(self);
+ return state->object_nl ? rb_str_new2(state->object_nl) : rb_str_new2("");
+}
+
+/*
+ * call-seq: object_nl=(object_nl)
+ *
+ * This string is put at the end of a line that holds a JSON object (or
+ * Hash).
+ */
+static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
+{
+ GET_STATE(self);
+ Check_Type(object_nl, T_STRING);
+ if (RSTRING_LEN(object_nl) == 0) {
+ if (state->object_nl) {
+ ruby_xfree(state->object_nl);
+ state->object_nl = NULL;
+ }
+ } else {
+ if (state->object_nl) ruby_xfree(state->object_nl);
+ state->object_nl = strdup(RSTRING_PTR(object_nl));
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: array_nl()
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_array_nl(VALUE self)
+{
+ GET_STATE(self);
+ return state->array_nl ? rb_str_new2(state->array_nl) : rb_str_new2("");
+}
+
+/*
+ * call-seq: array_nl=(array_nl)
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
+{
+ GET_STATE(self);
+ Check_Type(array_nl, T_STRING);
+ if (RSTRING_LEN(array_nl) == 0) {
+ if (state->array_nl) {
+ ruby_xfree(state->array_nl);
+ state->array_nl = NULL;
+ }
+ } else {
+ if (state->array_nl) ruby_xfree(state->array_nl);
+ state->array_nl = strdup(RSTRING_PTR(array_nl));
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq: max_nesting
+ *
+ * This integer returns the maximum level of data structure nesting in
+ * the generated JSON, max_nesting = 0 if no maximum is checked.
+ */
+static VALUE cState_max_nesting(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->max_nesting);
+}
+
+/*
+ * call-seq: max_nesting=(depth)
+ *
+ * This sets the maximum level of data structure nesting in the generated JSON
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
+ */
+static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
+{
+ GET_STATE(self);
+ Check_Type(depth, T_FIXNUM);
+ return state->max_nesting = FIX2LONG(depth);
+}
+
+/*
+ * call-seq: allow_nan?
+ *
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
+ * returns false.
+ */
+static VALUE cState_allow_nan_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->allow_nan ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: ascii_only?
+ *
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
+ * returns false.
+ */
+static VALUE cState_ascii_only_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->ascii_only ? Qtrue : Qfalse;
+}
+
+/*
+ *
+ */
+void Init_generator()
+{
+ rb_require("json/common");
+
+ mJSON = rb_define_module("JSON");
+ mExt = rb_define_module_under(mJSON, "Ext");
+ mGenerator = rb_define_module_under(mExt, "Generator");
+
+ eGeneratorError = rb_path2class("JSON::GeneratorError");
+ eNestingError = rb_path2class("JSON::NestingError");
+
+ cState = rb_define_class_under(mGenerator, "State", rb_cObject);
+ rb_define_alloc_func(cState, cState_s_allocate);
+ rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
+ rb_define_method(cState, "initialize", cState_initialize, -1);
+ rb_define_method(cState, "indent", cState_indent, 0);
+ rb_define_method(cState, "indent=", cState_indent_set, 1);
+ rb_define_method(cState, "space", cState_space, 0);
+ rb_define_method(cState, "space=", cState_space_set, 1);
+ rb_define_method(cState, "space_before", cState_space_before, 0);
+ rb_define_method(cState, "space_before=", cState_space_before_set, 1);
+ rb_define_method(cState, "object_nl", cState_object_nl, 0);
+ rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
+ rb_define_method(cState, "array_nl", cState_array_nl, 0);
+ rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
+ rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
+ rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
+ rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
+ rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
+ rb_define_method(cState, "configure", cState_configure, 1);
+ rb_define_method(cState, "to_h", cState_to_h, 0);
+ rb_define_method(cState, "generate", cState_generate, 1);
+ rb_define_method(cState, "partial_generate", cState_partial_generate, 1);
+
+ mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
+ mObject = rb_define_module_under(mGeneratorMethods, "Object");
+ rb_define_method(mObject, "to_json", mObject_to_json, -1);
+ mHash = rb_define_module_under(mGeneratorMethods, "Hash");
+ rb_define_method(mHash, "to_json", mHash_to_json, -1);
+ mArray = rb_define_module_under(mGeneratorMethods, "Array");
+ rb_define_method(mArray, "to_json", mArray_to_json, -1);
+ mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
+ rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
+ mFloat = rb_define_module_under(mGeneratorMethods, "Float");
+ rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
+ mString = rb_define_module_under(mGeneratorMethods, "String");
+ rb_define_singleton_method(mString, "included", mString_included_s, 1);
+ rb_define_method(mString, "to_json", mString_to_json, -1);
+ rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
+ rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
+ mString_Extend = rb_define_module_under(mString, "Extend");
+ rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
+ mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
+ rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
+ mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
+ rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
+ mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
+ rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
+
+ CRegexp_MULTILINE = rb_const_get(rb_cRegexp, rb_intern("MULTILINE"));
+ i_to_s = rb_intern("to_s");
+ i_to_json = rb_intern("to_json");
+ i_new = rb_intern("new");
+ i_indent = rb_intern("indent");
+ i_space = rb_intern("space");
+ i_space_before = rb_intern("space_before");
+ i_object_nl = rb_intern("object_nl");
+ i_array_nl = rb_intern("array_nl");
+ i_max_nesting = rb_intern("max_nesting");
+ i_allow_nan = rb_intern("allow_nan");
+ i_ascii_only = rb_intern("ascii_only");
+ i_pack = rb_intern("pack");
+ i_unpack = rb_intern("unpack");
+ i_create_id = rb_intern("create_id");
+ i_extend = rb_intern("extend");
+#ifdef HAVE_RUBY_ENCODING_H
+ CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
+ i_encoding = rb_intern("encoding");
+ i_encode = rb_intern("encode");
+#endif
+}
diff --git a/ext/json/ext/generator.h b/ext/json/ext/generator.h
new file mode 100644
index 0000000..5b5a28c
--- /dev/null
+++ b/ext/json/ext/generator.h
@@ -0,0 +1,178 @@
+#ifndef _GENERATOR_H_
+#define _GENERATOR_H_
+
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "ruby.h"
+
+#if HAVE_RUBY_ST_H
+#include "ruby/st.h"
+#endif
+
+#if HAVE_ST_H
+#include "st.h"
+#endif
+
+#if HAVE_RUBY_RE_H
+#include "ruby/re.h"
+#endif
+
+#if HAVE_RE_H
+#include "re.h"
+#endif
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
+#else
+#define FORCE_UTF8(obj)
+#endif
+
+#ifndef RHASH_TBL
+#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
+#endif
+
+#ifndef RHASH_SIZE
+#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
+#endif
+
+#ifndef RFLOAT_VALUE
+#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
+#endif
+
+#ifndef RARRAY_PTR
+#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
+#endif
+#ifndef RARRAY_LEN
+#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
+#endif
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(string) RSTRING(string)->ptr
+#endif
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(string) RSTRING(string)->len
+#endif
+
+#define RSTRING_PAIR(string) RSTRING_PTR(string), RSTRING_LEN(string)
+
+/* fbuffer implementation */
+
+typedef struct FBufferStruct {
+ unsigned int initial_length;
+ char *ptr;
+ unsigned int len;
+ unsigned int capa;
+} FBuffer;
+
+#define FBUFFER_INITIAL_LENGTH 4096
+
+#define FBUFFER_PTR(fb) (fb->ptr)
+#define FBUFFER_LEN(fb) (fb->len)
+#define FBUFFER_CAPA(fb) (fb->capa)
+#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
+
+static FBuffer *fbuffer_alloc();
+static FBuffer *fbuffer_alloc_with_length(unsigned initial_length);
+static void fbuffer_free(FBuffer *fb);
+static void fbuffer_free_only_buffer(FBuffer *fb);
+static void fbuffer_clear(FBuffer *fb);
+static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned int len);
+static void fbuffer_append_char(FBuffer *fb, char newchr);
+
+/* unicode defintions */
+
+#define UNI_STRICT_CONVERSION 1
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+static unsigned char isLegalUTF8(const UTF8 *source, int length);
+static void unicode_escape(char *buf, UTF16 character);
+static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character);
+static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string);
+static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string);
+
+/* ruby api and some helpers */
+
+typedef struct JSON_Generator_StateStruct {
+ char *indent;
+ long indent_len;
+ char *space;
+ long space_len;
+ char *space_before;
+ long space_before_len;
+ char *object_nl;
+ long object_nl_len;
+ char *array_nl;
+ long array_nl_len;
+ FBuffer *array_delim;
+ FBuffer *object_delim;
+ FBuffer *object_delim2;
+ long max_nesting;
+ char allow_nan;
+ char ascii_only;
+} JSON_Generator_State;
+
+#define GET_STATE(self) \
+ JSON_Generator_State *state; \
+ Data_Get_Struct(self, JSON_Generator_State, state)
+
+static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mString_included_s(VALUE self, VALUE modul);
+static VALUE mString_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mString_to_json_raw_object(VALUE self);
+static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self);
+static VALUE mString_Extend_json_create(VALUE self, VALUE o);
+static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self);
+static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self);
+static void State_free(JSON_Generator_State *state);
+static JSON_Generator_State *State_allocate();
+static VALUE cState_s_allocate(VALUE klass);
+static VALUE cState_configure(VALUE self, VALUE opts);
+static VALUE cState_to_h(VALUE self);
+static VALUE fbuffer2rstring(FBuffer *buffer);
+static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth);
+static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth);
+static VALUE cState_generate(VALUE self, VALUE obj);
+static VALUE cState_initialize(int argc, VALUE *argv, VALUE self);
+static VALUE cState_from_state_s(VALUE self, VALUE opts);
+static VALUE cState_indent(VALUE self);
+static VALUE cState_indent_set(VALUE self, VALUE indent);
+static VALUE cState_space(VALUE self);
+static VALUE cState_space_set(VALUE self, VALUE space);
+static VALUE cState_space_before(VALUE self);
+static VALUE cState_space_before_set(VALUE self, VALUE space_before);
+static VALUE cState_object_nl(VALUE self);
+static VALUE cState_object_nl_set(VALUE self, VALUE object_nl);
+static VALUE cState_array_nl(VALUE self);
+static VALUE cState_array_nl_set(VALUE self, VALUE array_nl);
+static VALUE cState_max_nesting(VALUE self);
+static VALUE cState_max_nesting_set(VALUE self, VALUE depth);
+static VALUE cState_allow_nan_p(VALUE self);
+static VALUE cState_ascii_only_p(VALUE self);
+
+#endif
diff --git a/ext/json/ext/generator/extconf.rb b/ext/json/ext/generator/extconf.rb
deleted file mode 100644
index 797b566..0000000
--- a/ext/json/ext/generator/extconf.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require 'mkmf'
-require 'rbconfig'
-
-if CONFIG['CC'] =~ /gcc/
- $CFLAGS += ' -Wall'
- #$CFLAGS += ' -O0 -ggdb'
-end
-
-have_header("ruby/st.h") || have_header("st.h")
-have_header("ruby/encoding.h")
-create_makefile 'generator'
diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c
deleted file mode 100644
index 558f28d..0000000
--- a/ext/json/ext/generator/generator.c
+++ /dev/null
@@ -1,935 +0,0 @@
-#include <string.h>
-#include "ruby.h"
-#if HAVE_RUBY_ST_H
-#include "ruby/st.h"
-#endif
-#if HAVE_ST_H
-#include "st.h"
-#endif
-#include "unicode.h"
-#include <math.h>
-
-#ifndef RHASH_TBL
-#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
-#endif
-
-#ifndef RHASH_SIZE
-#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
-#endif
-
-#ifndef RFLOAT_VALUE
-#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
-#endif
-
-#ifdef HAVE_RUBY_ENCODING_H
-#include "ruby/encoding.h"
-#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
-static VALUE mEncoding_UTF_8;
-static ID i_encoding, i_encode;
-#else
-#define FORCE_UTF8(obj)
-#endif
-
-#define check_max_nesting(state, depth) do { \
- long current_nesting = 1 + depth; \
- if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
- rb_raise(eNestingError, "nesting of %ld is too deep", current_nesting); \
-} while (0);
-
-static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
- mHash, mArray, mInteger, mFloat, mString, mString_Extend,
- mTrueClass, mFalseClass, mNilClass, eGeneratorError,
- eCircularDatastructure, eNestingError;
-
-static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
- i_object_nl, i_array_nl, i_check_circular, i_max_nesting,
- i_allow_nan, i_pack, i_unpack, i_create_id, i_extend;
-
-typedef struct JSON_Generator_StateStruct {
- VALUE indent;
- VALUE space;
- VALUE space_before;
- VALUE object_nl;
- VALUE array_nl;
- int check_circular;
- VALUE seen;
- VALUE memo;
- VALUE depth;
- long max_nesting;
- int flag;
- int allow_nan;
-} JSON_Generator_State;
-
-#define GET_STATE(self) \
- JSON_Generator_State *state; \
- Data_Get_Struct(self, JSON_Generator_State, state);
-
-/*
- * Document-module: JSON::Ext::Generator
- *
- * This is the JSON generator implemented as a C extension. It can be
- * configured to be used by setting
- *
- * JSON.generator = JSON::Ext::Generator
- *
- * with the method generator= in JSON.
- *
- */
-
-static int hash_to_json_state_i(VALUE key, VALUE value, VALUE Vstate)
-{
- VALUE json, buf, Vdepth;
- GET_STATE(Vstate);
- buf = state->memo;
- Vdepth = state->depth;
-
- if (key == Qundef) return ST_CONTINUE;
- if (state->flag) {
- state->flag = 0;
- rb_str_buf_cat2(buf, ",");
- if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(buf, state->object_nl);
- }
- if (RSTRING_LEN(state->object_nl)) {
- rb_str_buf_append(buf, rb_str_times(state->indent, Vdepth));
- }
- json = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 2, Vstate, Vdepth);
- Check_Type(json, T_STRING);
- rb_str_buf_append(buf, json);
- OBJ_INFECT(buf, json);
- if (RSTRING_LEN(state->space_before)) {
- rb_str_buf_append(buf, state->space_before);
- }
- rb_str_buf_cat2(buf, ":");
- if (RSTRING_LEN(state->space)) rb_str_buf_append(buf, state->space);
- json = rb_funcall(value, i_to_json, 2, Vstate, Vdepth);
- Check_Type(json, T_STRING);
- state->flag = 1;
- rb_str_buf_append(buf, json);
- OBJ_INFECT(buf, json);
- state->depth = Vdepth;
- state->memo = buf;
- return ST_CONTINUE;
-}
-
-inline static VALUE mHash_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
- long depth, len = RHASH_SIZE(self);
- VALUE result;
- GET_STATE(Vstate);
-
- depth = 1 + FIX2LONG(Vdepth);
- result = rb_str_buf_new(len);
- state->memo = result;
- state->depth = LONG2FIX(depth);
- state->flag = 0;
- rb_str_buf_cat2(result, "{");
- if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
- rb_hash_foreach(self, hash_to_json_state_i, Vstate);
- if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
- if (RSTRING_LEN(state->object_nl)) {
- rb_str_buf_append(result, rb_str_times(state->indent, Vdepth));
- }
- rb_str_buf_cat2(result, "}");
- return result;
-}
-
-static int hash_to_json_i(VALUE key, VALUE value, VALUE buf)
-{
- VALUE tmp;
-
- if (key == Qundef) return ST_CONTINUE;
- if (RSTRING_LEN(buf) > 1) rb_str_buf_cat2(buf, ",");
- tmp = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 0);
- Check_Type(tmp, T_STRING);
- rb_str_buf_append(buf, tmp);
- OBJ_INFECT(buf, tmp);
- rb_str_buf_cat2(buf, ":");
- tmp = rb_funcall(value, i_to_json, 0);
- Check_Type(tmp, T_STRING);
- rb_str_buf_append(buf, tmp);
- OBJ_INFECT(buf, tmp);
-
- return ST_CONTINUE;
-}
-
-/*
- * call-seq: to_json(state = nil, depth = 0)
- *
- * Returns a JSON string containing a JSON object, that is unparsed from
- * this Hash instance.
- * _state_ is a JSON::State object, that can also be used to configure the
- * produced JSON string output further.
- * _depth_ is used to find out nesting depth, to indent accordingly.
- */
-static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
-{
- VALUE Vstate, Vdepth, result;
- long depth;
-
- rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
- depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
- if (NIL_P(Vstate)) {
- long len = RHASH_SIZE(self);
- result = rb_str_buf_new(len);
- rb_str_buf_cat2(result, "{");
- rb_hash_foreach(self, hash_to_json_i, result);
- rb_str_buf_cat2(result, "}");
- } else {
- GET_STATE(Vstate);
- check_max_nesting(state, depth);
- if (state->check_circular) {
- VALUE self_id = rb_obj_id(self);
- if (RTEST(rb_hash_aref(state->seen, self_id))) {
- rb_raise(eCircularDatastructure,
- "circular data structures not supported!");
- }
- rb_hash_aset(state->seen, self_id, Qtrue);
- result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
- rb_hash_delete(state->seen, self_id);
- } else {
- result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
- }
- }
- OBJ_INFECT(result, self);
- FORCE_UTF8(result);
- return result;
-}
-
-inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
- long i, len = RARRAY_LEN(self);
- VALUE shift, result;
- long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
- VALUE delim = rb_str_new2(",");
- GET_STATE(Vstate);
-
- check_max_nesting(state, depth);
- if (state->check_circular) {
- VALUE self_id = rb_obj_id(self);
- rb_hash_aset(state->seen, self_id, Qtrue);
- result = rb_str_buf_new(len);
- if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
- shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
-
- rb_str_buf_cat2(result, "[");
- OBJ_INFECT(result, self);
- rb_str_buf_append(result, state->array_nl);
- for (i = 0; i < len; i++) {
- VALUE element = RARRAY_PTR(self)[i];
- if (RTEST(rb_hash_aref(state->seen, rb_obj_id(element)))) {
- rb_raise(eCircularDatastructure,
- "circular data structures not supported!");
- }
- OBJ_INFECT(result, element);
- if (i > 0) rb_str_buf_append(result, delim);
- rb_str_buf_append(result, shift);
- element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
- Check_Type(element, T_STRING);
- rb_str_buf_append(result, element);
- }
- if (RSTRING_LEN(state->array_nl)) {
- rb_str_buf_append(result, state->array_nl);
- rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
- }
- rb_str_buf_cat2(result, "]");
- rb_hash_delete(state->seen, self_id);
- } else {
- result = rb_str_buf_new(len);
- OBJ_INFECT(result, self);
- if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
- shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
-
- rb_str_buf_cat2(result, "[");
- rb_str_buf_append(result, state->array_nl);
- for (i = 0; i < len; i++) {
- VALUE element = RARRAY_PTR(self)[i];
- OBJ_INFECT(result, element);
- if (i > 0) rb_str_buf_append(result, delim);
- rb_str_buf_append(result, shift);
- element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
- Check_Type(element, T_STRING);
- rb_str_buf_append(result, element);
- }
- rb_str_buf_append(result, state->array_nl);
- if (RSTRING_LEN(state->array_nl)) {
- rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
- }
- rb_str_buf_cat2(result, "]");
- }
- return result;
-}
-
-/*
- * call-seq: to_json(state = nil, depth = 0)
- *
- * Returns a JSON string containing a JSON array, that is unparsed from
- * this Array instance.
- * _state_ is a JSON::State object, that can also be used to configure the
- * produced JSON string output further.
- * _depth_ is used to find out nesting depth, to indent accordingly.
- */
-static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
- VALUE Vstate, Vdepth, result;
-
- rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
- if (NIL_P(Vstate)) {
- long i, len = RARRAY_LEN(self);
- result = rb_str_buf_new(2 + 2 * len);
- rb_str_buf_cat2(result, "[");
- OBJ_INFECT(result, self);
- for (i = 0; i < len; i++) {
- VALUE element = RARRAY_PTR(self)[i];
- OBJ_INFECT(result, element);
- if (i > 0) rb_str_buf_cat2(result, ",");
- element = rb_funcall(element, i_to_json, 0);
- Check_Type(element, T_STRING);
- rb_str_buf_append(result, element);
- }
- rb_str_buf_cat2(result, "]");
- } else {
- result = mArray_json_transfrom(self, Vstate, Vdepth);
- }
- OBJ_INFECT(result, self);
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json(*)
- *
- * Returns a JSON string representation for this Integer number.
- */
-static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
-{
- VALUE result = rb_funcall(self, i_to_s, 0);
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json(*)
- *
- * Returns a JSON string representation for this Float number.
- */
-static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
-{
- JSON_Generator_State *state = NULL;
- VALUE Vstate, rest, tmp, result;
- double value = RFLOAT_VALUE(self);
- rb_scan_args(argc, argv, "01*", &Vstate, &rest);
- if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
- if (isinf(value)) {
- if (state && state->allow_nan) {
- result = rb_funcall(self, i_to_s, 0);
- } else {
- tmp = rb_funcall(self, i_to_s, 0);
- rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
- }
- } else if (isnan(value)) {
- if (state && state->allow_nan) {
- result = rb_funcall(self, i_to_s, 0);
- } else {
- tmp = rb_funcall(self, i_to_s, 0);
- rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
- }
- } else {
- result = rb_funcall(self, i_to_s, 0);
- }
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: String.included(modul)
- *
- * Extends _modul_ with the String::Extend module.
- */
-static VALUE mString_included_s(VALUE self, VALUE modul) {
- VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json(*)
- *
- * This string should be encoded with UTF-8 A call to this method
- * returns a JSON string encoded with UTF16 big endian characters as
- * \u????.
- */
-static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
-{
- VALUE result = rb_str_buf_new(RSTRING_LEN(self));
- rb_str_buf_cat2(result, "\"");
-#ifdef HAVE_RUBY_ENCODING_H
- if (rb_funcall(self, i_encoding, 0) == mEncoding_UTF_8) {
- JSON_convert_UTF8_to_JSON(result, self, strictConversion);
- } else {
- VALUE string = rb_funcall(self, i_encode, 1, mEncoding_UTF_8);
- JSON_convert_UTF8_to_JSON(result, string, strictConversion);
- }
-#else
- JSON_convert_UTF8_to_JSON(result, self, strictConversion);
-#endif
- rb_str_buf_cat2(result, "\"");
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json_raw_object()
- *
- * This method creates a raw object hash, that can be nested into
- * other data structures and will be unparsed as a raw string. This
- * method should be used, if you want to convert raw strings to JSON
- * instead of UTF-8 strings, e. g. binary data.
- */
-static VALUE mString_to_json_raw_object(VALUE self) {
- VALUE ary;
- VALUE result = rb_hash_new();
- rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
- ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
- rb_hash_aset(result, rb_str_new2("raw"), ary);
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json_raw(*args)
- *
- * This method creates a JSON text from the result of a call to
- * to_json_raw_object of this String.
- */
-static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
- VALUE result, obj = mString_to_json_raw_object(self);
- Check_Type(obj, T_HASH);
- result = mHash_to_json(argc, argv, obj);
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: json_create(o)
- *
- * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
- * key "raw"). The Ruby String can be created by this module method.
- */
-static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
- VALUE ary;
- Check_Type(o, T_HASH);
- ary = rb_hash_aref(o, rb_str_new2("raw"));
- return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
-}
-
-/*
- * call-seq: to_json(*)
- *
- * Returns a JSON string for true: 'true'.
- */
-static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
-{
- VALUE result = rb_str_new2("true");
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json(*)
- *
- * Returns a JSON string for false: 'false'.
- */
-static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
-{
- VALUE result = rb_str_new2("false");
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json(*)
- *
- */
-static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
-{
- VALUE result = rb_str_new2("null");
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * call-seq: to_json(*)
- *
- * Converts this object to a string (calling #to_s), converts
- * it to a JSON string, and returns the result. This is a fallback, if no
- * special method #to_json was defined for some object.
- */
-static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
-{
- VALUE result, string = rb_funcall(self, i_to_s, 0);
- Check_Type(string, T_STRING);
- result = mString_to_json(argc, argv, string);
- FORCE_UTF8(result);
- return result;
-}
-
-/*
- * Document-class: JSON::Ext::Generator::State
- *
- * This class is used to create State instances, that are use to hold data
- * while generating a JSON text from a a Ruby data structure.
- */
-
-static void State_mark(JSON_Generator_State *state)
-{
- rb_gc_mark_maybe(state->indent);
- rb_gc_mark_maybe(state->space);
- rb_gc_mark_maybe(state->space_before);
- rb_gc_mark_maybe(state->object_nl);
- rb_gc_mark_maybe(state->array_nl);
- rb_gc_mark_maybe(state->seen);
- rb_gc_mark_maybe(state->memo);
- rb_gc_mark_maybe(state->depth);
-}
-
-static JSON_Generator_State *State_allocate()
-{
- JSON_Generator_State *state = ALLOC(JSON_Generator_State);
- return state;
-}
-
-static VALUE cState_s_allocate(VALUE klass)
-{
- JSON_Generator_State *state = State_allocate();
- return Data_Wrap_Struct(klass, State_mark, -1, state);
-}
-
-/*
- * call-seq: configure(opts)
- *
- * Configure this State instance with the Hash _opts_, and return
- * itself.
- */
-static VALUE cState_configure(VALUE self, VALUE opts)
-{
- VALUE tmp;
- GET_STATE(self);
- tmp = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
- if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
- if (NIL_P(tmp)) {
- rb_raise(rb_eArgError, "opts has to be hash like or convertable into a hash");
- }
- opts = tmp;
- tmp = rb_hash_aref(opts, ID2SYM(i_indent));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->indent = tmp;
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_space));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->space = tmp;
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->space_before = tmp;
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->array_nl = tmp;
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
- if (RTEST(tmp)) {
- Check_Type(tmp, T_STRING);
- state->object_nl = tmp;
- }
- tmp = ID2SYM(i_check_circular);
- if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
- tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
- state->check_circular = RTEST(tmp);
- } else {
- state->check_circular = 1;
- }
- tmp = ID2SYM(i_max_nesting);
- state->max_nesting = 19;
- if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
- VALUE max_nesting = rb_hash_aref(opts, tmp);
- if (RTEST(max_nesting)) {
- Check_Type(max_nesting, T_FIXNUM);
- state->max_nesting = FIX2LONG(max_nesting);
- } else {
- state->max_nesting = 0;
- }
- }
- tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
- state->allow_nan = RTEST(tmp);
- return self;
-}
-
-/*
- * call-seq: to_h
- *
- * Returns the configuration instance variables as a hash, that can be
- * passed to the configure method.
- */
-static VALUE cState_to_h(VALUE self)
-{
- VALUE result = rb_hash_new();
- GET_STATE(self);
- rb_hash_aset(result, ID2SYM(i_indent), state->indent);
- rb_hash_aset(result, ID2SYM(i_space), state->space);
- rb_hash_aset(result, ID2SYM(i_space_before), state->space_before);
- rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl);
- rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl);
- rb_hash_aset(result, ID2SYM(i_check_circular), state->check_circular ? Qtrue : Qfalse);
- rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
- rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
- return result;
-}
-
-
-/*
- * call-seq: new(opts = {})
- *
- * Instantiates a new State object, configured by _opts_.
- *
- * _opts_ can have the following keys:
- *
- * * *indent*: a string used to indent levels (default: ''),
- * * *space*: a string that is put after, a : or , delimiter (default: ''),
- * * *space_before*: a string that is put before a : pair delimiter (default: ''),
- * * *object_nl*: a string that is put at the end of a JSON object (default: ''),
- * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
- * * *check_circular*: true if checking for circular data structures
- * should be done, false (the default) otherwise.
- * * *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.
- */
-static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
-{
- VALUE opts;
- GET_STATE(self);
-
- rb_scan_args(argc, argv, "01", &opts);
- state->indent = rb_str_new2("");
- state->space = rb_str_new2("");
- state->space_before = rb_str_new2("");
- state->array_nl = rb_str_new2("");
- state->object_nl = rb_str_new2("");
- if (NIL_P(opts)) {
- state->check_circular = 1;
- state->allow_nan = 0;
- state->max_nesting = 19;
- } else {
- cState_configure(self, opts);
- }
- state->seen = rb_hash_new();
- state->memo = Qnil;
- state->depth = INT2FIX(0);
- return self;
-}
-
-/*
- * call-seq: from_state(opts)
- *
- * Creates a State object from _opts_, which ought to be Hash to create a
- * new State instance configured by _opts_, something else to create an
- * unconfigured instance. If _opts_ is a State object, it is just returned.
- */
-static VALUE cState_from_state_s(VALUE self, VALUE opts)
-{
- if (rb_obj_is_kind_of(opts, self)) {
- return opts;
- } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
- return rb_funcall(self, i_new, 1, opts);
- } else {
- return rb_funcall(self, i_new, 0);
- }
-}
-
-/*
- * call-seq: indent()
- *
- * This string is used to indent levels in the JSON text.
- */
-static VALUE cState_indent(VALUE self)
-{
- GET_STATE(self);
- return state->indent;
-}
-
-/*
- * call-seq: indent=(indent)
- *
- * This string is used to indent levels in the JSON text.
- */
-static VALUE cState_indent_set(VALUE self, VALUE indent)
-{
- GET_STATE(self);
- Check_Type(indent, T_STRING);
- return state->indent = indent;
-}
-
-/*
- * call-seq: space()
- *
- * This string is used to insert a space between the tokens in a JSON
- * string.
- */
-static VALUE cState_space(VALUE self)
-{
- GET_STATE(self);
- return state->space;
-}
-
-/*
- * call-seq: space=(space)
- *
- * This string is used to insert a space between the tokens in a JSON
- * string.
- */
-static VALUE cState_space_set(VALUE self, VALUE space)
-{
- GET_STATE(self);
- Check_Type(space, T_STRING);
- return state->space = space;
-}
-
-/*
- * call-seq: space_before()
- *
- * This string is used to insert a space before the ':' in JSON objects.
- */
-static VALUE cState_space_before(VALUE self)
-{
- GET_STATE(self);
- return state->space_before;
-}
-
-/*
- * call-seq: space_before=(space_before)
- *
- * This string is used to insert a space before the ':' in JSON objects.
- */
-static VALUE cState_space_before_set(VALUE self, VALUE space_before)
-{
- GET_STATE(self);
- Check_Type(space_before, T_STRING);
- return state->space_before = space_before;
-}
-
-/*
- * call-seq: object_nl()
- *
- * This string is put at the end of a line that holds a JSON object (or
- * Hash).
- */
-static VALUE cState_object_nl(VALUE self)
-{
- GET_STATE(self);
- return state->object_nl;
-}
-
-/*
- * call-seq: object_nl=(object_nl)
- *
- * This string is put at the end of a line that holds a JSON object (or
- * Hash).
- */
-static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
-{
- GET_STATE(self);
- Check_Type(object_nl, T_STRING);
- return state->object_nl = object_nl;
-}
-
-/*
- * call-seq: array_nl()
- *
- * This string is put at the end of a line that holds a JSON array.
- */
-static VALUE cState_array_nl(VALUE self)
-{
- GET_STATE(self);
- return state->array_nl;
-}
-
-/*
- * call-seq: array_nl=(array_nl)
- *
- * This string is put at the end of a line that holds a JSON array.
- */
-static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
-{
- GET_STATE(self);
- Check_Type(array_nl, T_STRING);
- return state->array_nl = array_nl;
-}
-
-/*
- * call-seq: check_circular?
- *
- * Returns true, if circular data structures should be checked,
- * otherwise returns false.
- */
-static VALUE cState_check_circular_p(VALUE self)
-{
- GET_STATE(self);
- return state->check_circular ? Qtrue : Qfalse;
-}
-
-/*
- * call-seq: max_nesting
- *
- * This integer returns the maximum level of data structure nesting in
- * the generated JSON, max_nesting = 0 if no maximum is checked.
- */
-static VALUE cState_max_nesting(VALUE self)
-{
- GET_STATE(self);
- return LONG2FIX(state->max_nesting);
-}
-
-/*
- * call-seq: max_nesting=(depth)
- *
- * This sets the maximum level of data structure nesting in the generated JSON
- * to the integer depth, max_nesting = 0 if no maximum should be checked.
- */
-static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
-{
- GET_STATE(self);
- Check_Type(depth, T_FIXNUM);
- state->max_nesting = FIX2LONG(depth);
- return Qnil;
-}
-
-/*
- * call-seq: allow_nan?
- *
- * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
- * returns false.
- */
-static VALUE cState_allow_nan_p(VALUE self)
-{
- GET_STATE(self);
- return state->allow_nan ? Qtrue : Qfalse;
-}
-
-/*
- * call-seq: seen?(object)
- *
- * Returns _true_, if _object_ was already seen during this generating run.
- */
-static VALUE cState_seen_p(VALUE self, VALUE object)
-{
- GET_STATE(self);
- return rb_hash_aref(state->seen, rb_obj_id(object));
-}
-
-/*
- * call-seq: remember(object)
- *
- * Remember _object_, to find out if it was already encountered (if a cyclic
- * data structure is rendered).
- */
-static VALUE cState_remember(VALUE self, VALUE object)
-{
- GET_STATE(self);
- return rb_hash_aset(state->seen, rb_obj_id(object), Qtrue);
-}
-
-/*
- * call-seq: forget(object)
- *
- * Forget _object_ for this generating run.
- */
-static VALUE cState_forget(VALUE self, VALUE object)
-{
- GET_STATE(self);
- return rb_hash_delete(state->seen, rb_obj_id(object));
-}
-
-/*
- *
- */
-void Init_generator()
-{
- rb_require("json/common");
- mJSON = rb_define_module("JSON");
- mExt = rb_define_module_under(mJSON, "Ext");
- mGenerator = rb_define_module_under(mExt, "Generator");
- eGeneratorError = rb_path2class("JSON::GeneratorError");
- eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
- eNestingError = rb_path2class("JSON::NestingError");
- cState = rb_define_class_under(mGenerator, "State", rb_cObject);
- rb_define_alloc_func(cState, cState_s_allocate);
- rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
- rb_define_method(cState, "initialize", cState_initialize, -1);
-
- rb_define_method(cState, "indent", cState_indent, 0);
- rb_define_method(cState, "indent=", cState_indent_set, 1);
- rb_define_method(cState, "space", cState_space, 0);
- rb_define_method(cState, "space=", cState_space_set, 1);
- rb_define_method(cState, "space_before", cState_space_before, 0);
- rb_define_method(cState, "space_before=", cState_space_before_set, 1);
- rb_define_method(cState, "object_nl", cState_object_nl, 0);
- rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
- rb_define_method(cState, "array_nl", cState_array_nl, 0);
- rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
- rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
- rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
- rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
- rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
- rb_define_method(cState, "seen?", cState_seen_p, 1);
- rb_define_method(cState, "remember", cState_remember, 1);
- rb_define_method(cState, "forget", cState_forget, 1);
- rb_define_method(cState, "configure", cState_configure, 1);
- rb_define_method(cState, "to_h", cState_to_h, 0);
-
- mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
- mObject = rb_define_module_under(mGeneratorMethods, "Object");
- rb_define_method(mObject, "to_json", mObject_to_json, -1);
- mHash = rb_define_module_under(mGeneratorMethods, "Hash");
- rb_define_method(mHash, "to_json", mHash_to_json, -1);
- mArray = rb_define_module_under(mGeneratorMethods, "Array");
- rb_define_method(mArray, "to_json", mArray_to_json, -1);
- mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
- rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
- mFloat = rb_define_module_under(mGeneratorMethods, "Float");
- rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
- mString = rb_define_module_under(mGeneratorMethods, "String");
- rb_define_singleton_method(mString, "included", mString_included_s, 1);
- rb_define_method(mString, "to_json", mString_to_json, -1);
- rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
- rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
- mString_Extend = rb_define_module_under(mString, "Extend");
- rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
- mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
- rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
- mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
- rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
- mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
- rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
-
- i_to_s = rb_intern("to_s");
- i_to_json = rb_intern("to_json");
- i_new = rb_intern("new");
- i_indent = rb_intern("indent");
- i_space = rb_intern("space");
- i_space_before = rb_intern("space_before");
- i_object_nl = rb_intern("object_nl");
- i_array_nl = rb_intern("array_nl");
- i_check_circular = rb_intern("check_circular");
- i_max_nesting = rb_intern("max_nesting");
- i_allow_nan = rb_intern("allow_nan");
- i_pack = rb_intern("pack");
- i_unpack = rb_intern("unpack");
- i_create_id = rb_intern("create_id");
- i_extend = rb_intern("extend");
-#ifdef HAVE_RUBY_ENCODING_H
- mEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
- i_encoding = rb_intern("encoding");
- i_encode = rb_intern("encode");
-#endif
-}
diff --git a/ext/json/ext/generator/unicode.c b/ext/json/ext/generator/unicode.c
deleted file mode 100644
index 3ddfbe0..0000000
--- a/ext/json/ext/generator/unicode.c
+++ /dev/null
@@ -1,180 +0,0 @@
-#include "unicode.h"
-
-#define unicode_escape(buffer, character) \
- snprintf(buf, 7, "\\u%04x", (unsigned int) (character)); \
- rb_str_buf_cat(buffer, buf, 6);
-
-/*
- * Copyright 2001-2004 Unicode, Inc.
- *
- * Disclaimer
- *
- * This source code is provided as is by Unicode, Inc. No claims are
- * made as to fitness for any particular purpose. No warranties of any
- * kind are expressed or implied. The recipient agrees to determine
- * applicability of information provided. If this file has been
- * purchased on magnetic or optical media from Unicode, Inc., the
- * sole remedy for any claim will be exchange of defective media
- * within 90 days of receipt.
- *
- * Limitations on Rights to Redistribute This Code
- *
- * Unicode, Inc. hereby grants the right to freely use the information
- * supplied in this file in the creation of products supporting the
- * Unicode Standard, and to make copies of this file in any form
- * for internal or external distribution as long as this notice
- * remains attached.
- */
-
-/*
- * Index into the table below with the first byte of a UTF-8 sequence to
- * get the number of trailing bytes that are supposed to follow it.
- * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
- * left as-is for anyone who may want to do such conversion, which was
- * allowed in earlier algorithms.
- */
-static const char trailingBytesForUTF8[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
-};
-
-/*
- * Magic values subtracted from a buffer value during UTF8 conversion.
- * This table contains as many values as there might be trailing bytes
- * in a UTF-8 sequence.
- */
-static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
- 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
-
-/*
- * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
- * into the first byte, depending on how many bytes follow. There are
- * as many entries in this table as there are UTF-8 sequence types.
- * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
- * for *legal* UTF-8 will be 4 or fewer bytes total.
- */
-static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
-
-/*
- * Utility routine to tell whether a sequence of bytes is legal UTF-8.
- * This must be called with the length pre-determined by the first byte.
- * If not calling this from ConvertUTF8to*, then the length can be set by:
- * length = trailingBytesForUTF8[*source]+1;
- * and the sequence is illegal right away if there aren't that many bytes
- * available.
- * If presented with a length > 4, this returns 0. The Unicode
- * definition of UTF-8 goes up to 4-byte sequences.
- */
-
-inline static unsigned char isLegalUTF8(const UTF8 *source, int length)
-{
- UTF8 a;
- const UTF8 *srcptr = source+length;
- switch (length) {
- default: return 0;
- /* Everything else falls through when "1"... */
- case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
- case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
- case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
-
- switch (*source) {
- /* no fall-through in this inner switch */
- case 0xE0: if (a < 0xA0) return 0; break;
- case 0xED: if (a > 0x9F) return 0; break;
- case 0xF0: if (a < 0x90) return 0; break;
- case 0xF4: if (a > 0x8F) return 0; break;
- default: if (a < 0x80) return 0;
- }
-
- case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
- }
- if (*source > 0xF4) return 0;
- return 1;
-}
-
-void JSON_convert_UTF8_to_JSON(VALUE buffer, VALUE string, ConversionFlags flags)
-{
- char buf[7];
- const UTF8* source = (UTF8 *) RSTRING_PTR(string);
- const UTF8* sourceEnd = source + RSTRING_LEN(string);
-
- while (source < sourceEnd) {
- UTF32 ch = 0;
- unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
- if (source + extraBytesToRead >= sourceEnd) {
- rb_raise(rb_path2class("JSON::GeneratorError"),
- "partial character in source, but hit end");
- }
- if (!isLegalUTF8(source, extraBytesToRead+1)) {
- rb_raise(rb_path2class("JSON::GeneratorError"),
- "source sequence is illegal/malformed utf-8");
- }
- /*
- * The cases all fall through. See "Note A" below.
- */
- switch (extraBytesToRead) {
- case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
- case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
- case 3: ch += *source++; ch <<= 6;
- case 2: ch += *source++; ch <<= 6;
- case 1: ch += *source++; ch <<= 6;
- case 0: ch += *source++;
- }
- ch -= offsetsFromUTF8[extraBytesToRead];
-
- if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
- /* UTF-16 surrogate values are illegal in UTF-32 */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
- if (flags == strictConversion) {
- source -= (extraBytesToRead+1); /* return to the illegal value itself */
- rb_raise(rb_path2class("JSON::GeneratorError"),
- "source sequence is illegal/malformed utf-8");
- } else {
- unicode_escape(buffer, UNI_REPLACEMENT_CHAR);
- }
- } else {
- /* normal case */
- if (ch == '"') {
- rb_str_buf_cat2(buffer, "\\\"");
- } else if (ch == '\\') {
- rb_str_buf_cat2(buffer, "\\\\");
- } else if (ch >= 0x20 && ch <= 0x7f) {
- rb_str_buf_cat(buffer, (char *) source - 1, 1);
- } else if (ch == '\n') {
- rb_str_buf_cat2(buffer, "\\n");
- } else if (ch == '\r') {
- rb_str_buf_cat2(buffer, "\\r");
- } else if (ch == '\t') {
- rb_str_buf_cat2(buffer, "\\t");
- } else if (ch == '\f') {
- rb_str_buf_cat2(buffer, "\\f");
- } else if (ch == '\b') {
- rb_str_buf_cat2(buffer, "\\b");
- } else if (ch < 0x20) {
- unicode_escape(buffer, (UTF16) ch);
- } else {
- unicode_escape(buffer, (UTF16) ch);
- }
- }
- } else if (ch > UNI_MAX_UTF16) {
- if (flags == strictConversion) {
- source -= (extraBytesToRead+1); /* return to the start */
- rb_raise(rb_path2class("JSON::GeneratorError"),
- "source sequence is illegal/malformed utf8");
- } else {
- unicode_escape(buffer, UNI_REPLACEMENT_CHAR);
- }
- } else {
- /* target is a character in range 0xFFFF - 0x10FFFF. */
- ch -= halfBase;
- unicode_escape(buffer, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START));
- unicode_escape(buffer, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
- }
- }
-}
diff --git a/ext/json/ext/generator/unicode.h b/ext/json/ext/generator/unicode.h
deleted file mode 100644
index 841474b..0000000
--- a/ext/json/ext/generator/unicode.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#include "ruby.h"
-
-#ifndef _GENERATOR_UNICODE_H_
-#define _GENERATOR_UNICODE_H_
-
-typedef enum {
- conversionOK = 0, /* conversion successful */
- sourceExhausted, /* partial character in source, but hit end */
- targetExhausted, /* insuff. room in target for conversion */
- sourceIllegal /* source sequence is illegal/malformed */
-} ConversionResult;
-
-typedef enum {
- strictConversion = 0,
- lenientConversion
-} ConversionFlags;
-
-typedef unsigned long UTF32; /* at least 32 bits */
-typedef unsigned short UTF16; /* at least 16 bits */
-typedef unsigned char UTF8; /* typically 8 bits */
-
-#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
-#define UNI_MAX_BMP (UTF32)0x0000FFFF
-#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
-#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
-#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
-
-#define UNI_SUR_HIGH_START (UTF32)0xD800
-#define UNI_SUR_HIGH_END (UTF32)0xDBFF
-#define UNI_SUR_LOW_START (UTF32)0xDC00
-#define UNI_SUR_LOW_END (UTF32)0xDFFF
-
-static const int halfShift = 10; /* used for shifting by 10 bits */
-
-static const UTF32 halfBase = 0x0010000UL;
-static const UTF32 halfMask = 0x3FFUL;
-
-void JSON_convert_UTF8_to_JSON(VALUE buffer, VALUE string, ConversionFlags flags);
-
-#ifndef RARRAY_PTR
-#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
-#endif
-#ifndef RARRAY_LEN
-#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
-#endif
-#ifndef RSTRING_PTR
-#define RSTRING_PTR(string) RSTRING(string)->ptr
-#endif
-#ifndef RSTRING_LEN
-#define RSTRING_LEN(string) RSTRING(string)->len
-#endif
-
-#endif
diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser.c
index 1781381..0122571 100644
--- a/ext/json/ext/parser/parser.c
+++ b/ext/json/ext/parser.c
@@ -1,31 +1,77 @@
#line 1 "parser.rl"
-#include "ruby.h"
-#include "unicode.h"
-#if HAVE_RE_H
-#include "re.h"
-#endif
-#if HAVE_RUBY_ST_H
-#include "ruby/st.h"
-#endif
-#if HAVE_ST_H
-#include "st.h"
-#endif
+#include "parser.h"
+
+/* unicode */
+
+static const char digit_values[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+};
+
+static UTF32 unescape_unicode(const unsigned char *p)
+{
+ char b;
+ UTF32 result = 0;
+ b = digit_values[p[0]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | b;
+ b = digit_values[p[1]];
+ result = (result << 4) | b;
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ b = digit_values[p[2]];
+ result = (result << 4) | b;
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ b = digit_values[p[3]];
+ result = (result << 4) | b;
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ return result;
+}
-#define EVIL 0x666
+static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
+{
+ int len = 1;
+ if (ch <= 0x7F) {
+ buf[0] = (char) ch;
+ } else if (ch <= 0x07FF) {
+ buf[0] = (char) ((ch >> 6) | 0xC0);
+ buf[1] = (char) ((ch & 0x3F) | 0x80);
+ len++;
+ } else if (ch <= 0xFFFF) {
+ buf[0] = (char) ((ch >> 12) | 0xE0);
+ buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[2] = (char) ((ch & 0x3F) | 0x80);
+ len += 2;
+ } else if (ch <= 0x1fffff) {
+ buf[0] =(char) ((ch >> 18) | 0xF0);
+ buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
+ buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[3] =(char) ((ch & 0x3F) | 0x80);
+ len += 3;
+ } else {
+ buf[0] = '?';
+ }
+ return len;
+}
-#ifndef RHASH_TBL
-#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
-#endif
#ifdef HAVE_RUBY_ENCODING_H
-#include "ruby/encoding.h"
-#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
-static VALUE mEncoding_ASCII_8BIT, mEncoding_UTF_8, mEncoding_UTF_16BE,
- mEncoding_UTF_16LE, mEncoding_UTF_32BE, mEncoding_UTF_32LE;
+static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8, CEncoding_UTF_16BE,
+ CEncoding_UTF_16LE, CEncoding_UTF_32BE, CEncoding_UTF_32LE;
static ID i_encoding, i_encode, i_encode_bang, i_force_encoding;
#else
-#define FORCE_UTF8(obj)
static ID i_iconv;
#endif
@@ -35,38 +81,12 @@ 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_object_class, i_array_class;
-#define MinusInfinity "-Infinity"
-
-typedef struct JSON_ParserStruct {
- VALUE Vsource;
- char *source;
- long len;
- char *memo;
- VALUE create_id;
- int max_nesting;
- int current_nesting;
- int allow_nan;
- VALUE object_class;
- VALUE array_class;
-} JSON_Parser;
-
-static char *JSON_parse_object(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_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
-static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
-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);
-
-#define GET_STRUCT \
- JSON_Parser *json; \
- Data_Get_Struct(self, JSON_Parser, json);
+#line 108 "parser.rl"
-#line 88 "parser.rl"
-
-#line 70 "parser.c"
+#line 90 "parser.c"
static const int JSON_object_start = 1;
static const int JSON_object_first_final = 27;
static const int JSON_object_error = 0;
@@ -74,7 +94,7 @@ static const int JSON_object_error = 0;
static const int JSON_object_en_main = 1;
-#line 121 "parser.rl"
+#line 141 "parser.rl"
static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -90,14 +110,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
*result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class);
-#line 94 "parser.c"
+#line 114 "parser.c"
{
cs = JSON_object_start;
}
-#line 136 "parser.rl"
+#line 156 "parser.rl"
-#line 101 "parser.c"
+#line 121 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -125,7 +145,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 107 "parser.rl"
+#line 127 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, &last_name);
if (np == NULL) { p--; {p++; cs = 3; goto _out;} } else {p = (( np))-1;}
@@ -135,7 +155,7 @@ st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 139 "parser.c"
+#line 159 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -202,7 +222,7 @@ case 8:
goto st8;
goto st0;
tr11:
-#line 96 "parser.rl"
+#line 116 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v);
@@ -218,7 +238,7 @@ st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
-#line 222 "parser.c"
+#line 242 "parser.c"
switch( (*p) ) {
case 13: goto st9;
case 32: goto st9;
@@ -307,14 +327,14 @@ case 18:
goto st9;
goto st18;
tr4:
-#line 112 "parser.rl"
+#line 132 "parser.rl"
{ p--; {p++; cs = 27; goto _out;} }
goto st27;
st27:
if ( ++p == pe )
goto _test_eof27;
case 27:
-#line 318 "parser.c"
+#line 338 "parser.c"
goto st0;
st19:
if ( ++p == pe )
@@ -412,7 +432,7 @@ case 26:
_out: {}
}
-#line 137 "parser.rl"
+#line 157 "parser.rl"
if (cs >= JSON_object_first_final) {
if (RTEST(json->create_id)) {
@@ -431,7 +451,7 @@ case 26:
}
-#line 435 "parser.c"
+#line 455 "parser.c"
static const int JSON_value_start = 1;
static const int JSON_value_first_final = 21;
static const int JSON_value_error = 0;
@@ -439,7 +459,7 @@ static const int JSON_value_error = 0;
static const int JSON_value_en_main = 1;
-#line 235 "parser.rl"
+#line 255 "parser.rl"
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -447,14 +467,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 451 "parser.c"
+#line 471 "parser.c"
{
cs = JSON_value_start;
}
-#line 242 "parser.rl"
+#line 262 "parser.rl"
-#line 458 "parser.c"
+#line 478 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -479,14 +499,14 @@ st0:
cs = 0;
goto _out;
tr0:
-#line 183 "parser.rl"
+#line 203 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, result);
if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;}
}
goto st21;
tr2:
-#line 188 "parser.rl"
+#line 208 "parser.rl"
{
char *np;
if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) {
@@ -506,7 +526,7 @@ tr2:
}
goto st21;
tr5:
-#line 206 "parser.rl"
+#line 226 "parser.rl"
{
char *np;
json->current_nesting++;
@@ -516,7 +536,7 @@ tr5:
}
goto st21;
tr9:
-#line 214 "parser.rl"
+#line 234 "parser.rl"
{
char *np;
json->current_nesting++;
@@ -526,7 +546,7 @@ tr9:
}
goto st21;
tr16:
-#line 176 "parser.rl"
+#line 196 "parser.rl"
{
if (json->allow_nan) {
*result = CInfinity;
@@ -536,7 +556,7 @@ tr16:
}
goto st21;
tr18:
-#line 169 "parser.rl"
+#line 189 "parser.rl"
{
if (json->allow_nan) {
*result = CNaN;
@@ -546,19 +566,19 @@ tr18:
}
goto st21;
tr22:
-#line 163 "parser.rl"
+#line 183 "parser.rl"
{
*result = Qfalse;
}
goto st21;
tr25:
-#line 160 "parser.rl"
+#line 180 "parser.rl"
{
*result = Qnil;
}
goto st21;
tr28:
-#line 166 "parser.rl"
+#line 186 "parser.rl"
{
*result = Qtrue;
}
@@ -567,9 +587,9 @@ st21:
if ( ++p == pe )
goto _test_eof21;
case 21:
-#line 222 "parser.rl"
+#line 242 "parser.rl"
{ p--; {p++; cs = 21; goto _out;} }
-#line 573 "parser.c"
+#line 593 "parser.c"
goto st0;
st2:
if ( ++p == pe )
@@ -730,7 +750,7 @@ case 20:
_out: {}
}
-#line 243 "parser.rl"
+#line 263 "parser.rl"
if (cs >= JSON_value_first_final) {
return p;
@@ -740,7 +760,7 @@ case 20:
}
-#line 744 "parser.c"
+#line 764 "parser.c"
static const int JSON_integer_start = 1;
static const int JSON_integer_first_final = 5;
static const int JSON_integer_error = 0;
@@ -748,7 +768,7 @@ static const int JSON_integer_error = 0;
static const int JSON_integer_en_main = 1;
-#line 259 "parser.rl"
+#line 279 "parser.rl"
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -756,15 +776,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
int cs = EVIL;
-#line 760 "parser.c"
+#line 780 "parser.c"
{
cs = JSON_integer_start;
}
-#line 266 "parser.rl"
+#line 286 "parser.rl"
json->memo = p;
-#line 768 "parser.c"
+#line 788 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -798,14 +818,14 @@ case 3:
goto st0;
goto tr4;
tr4:
-#line 256 "parser.rl"
+#line 276 "parser.rl"
{ p--; {p++; cs = 5; goto _out;} }
goto st5;
st5:
if ( ++p == pe )
goto _test_eof5;
case 5:
-#line 809 "parser.c"
+#line 829 "parser.c"
goto st0;
st4:
if ( ++p == pe )
@@ -824,7 +844,7 @@ case 4:
_out: {}
}
-#line 268 "parser.rl"
+#line 288 "parser.rl"
if (cs >= JSON_integer_first_final) {
long len = p - json->memo;
@@ -836,7 +856,7 @@ case 4:
}
-#line 840 "parser.c"
+#line 860 "parser.c"
static const int JSON_float_start = 1;
static const int JSON_float_first_final = 10;
static const int JSON_float_error = 0;
@@ -844,7 +864,7 @@ static const int JSON_float_error = 0;
static const int JSON_float_en_main = 1;
-#line 290 "parser.rl"
+#line 310 "parser.rl"
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -852,15 +872,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 856 "parser.c"
+#line 876 "parser.c"
{
cs = JSON_float_start;
}
-#line 297 "parser.rl"
+#line 317 "parser.rl"
json->memo = p;
-#line 864 "parser.c"
+#line 884 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -918,14 +938,14 @@ case 5:
goto st0;
goto tr7;
tr7:
-#line 284 "parser.rl"
+#line 304 "parser.rl"
{ p--; {p++; cs = 10; goto _out;} }
goto st10;
st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 929 "parser.c"
+#line 949 "parser.c"
goto st0;
st6:
if ( ++p == pe )
@@ -986,7 +1006,7 @@ case 9:
_out: {}
}
-#line 299 "parser.rl"
+#line 319 "parser.rl"
if (cs >= JSON_float_first_final) {
long len = p - json->memo;
@@ -999,7 +1019,7 @@ case 9:
-#line 1003 "parser.c"
+#line 1023 "parser.c"
static const int JSON_array_start = 1;
static const int JSON_array_first_final = 17;
static const int JSON_array_error = 0;
@@ -1007,7 +1027,7 @@ static const int JSON_array_error = 0;
static const int JSON_array_en_main = 1;
-#line 335 "parser.rl"
+#line 355 "parser.rl"
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -1021,14 +1041,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
*result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
-#line 1025 "parser.c"
+#line 1045 "parser.c"
{
cs = JSON_array_start;
}
-#line 348 "parser.rl"
+#line 368 "parser.rl"
-#line 1032 "parser.c"
+#line 1052 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1067,7 +1087,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 316 "parser.rl"
+#line 336 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v);
@@ -1083,7 +1103,7 @@ st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 1087 "parser.c"
+#line 1107 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -1183,14 +1203,14 @@ case 12:
goto st3;
goto st12;
tr4:
-#line 327 "parser.rl"
+#line 347 "parser.rl"
{ p--; {p++; cs = 17; goto _out;} }
goto st17;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
-#line 1194 "parser.c"
+#line 1214 "parser.c"
goto st0;
st13:
if ( ++p == pe )
@@ -1246,7 +1266,7 @@ case 16:
_out: {}
}
-#line 349 "parser.rl"
+#line 369 "parser.rl"
if(cs >= JSON_array_first_final) {
return p + 1;
@@ -1255,64 +1275,78 @@ case 16:
}
}
-static VALUE json_string_unescape(char *p, char *pe)
+static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
{
- VALUE result = rb_str_buf_new(pe - p + 1);
-
- while (p < pe) {
- if (*p == '\\') {
- p++;
- if (p >= pe) return Qnil; /* raise an exception later, \ at end */
- switch (*p) {
+ char *p = string, *pe = string, *unescape;
+ int unescape_len;
+
+ while (pe < stringEnd) {
+ if (*pe == '\\') {
+ unescape = (char *) "?";
+ unescape_len = 1;
+ if (pe > p) rb_str_buf_cat(result, p, pe - p);
+ switch (*++pe) {
+ case 'n':
+ unescape = (char *) "\n";
+ break;
+ case 'r':
+ unescape = (char *) "\r";
+ break;
+ case 't':
+ unescape = (char *) "\t";
+ break;
case '"':
+ unescape = (char *) "\"";
+ break;
case '\\':
- rb_str_buf_cat(result, p, 1);
- p++;
+ unescape = (char *) "\\";
break;
case 'b':
- rb_str_buf_cat2(result, "\b");
- p++;
+ unescape = (char *) "\b";
break;
case 'f':
- rb_str_buf_cat2(result, "\f");
- p++;
- break;
- case 'n':
- rb_str_buf_cat2(result, "\n");
- p++;
- break;
- case 'r':
- rb_str_buf_cat2(result, "\r");
- p++;
- break;
- case 't':
- rb_str_buf_cat2(result, "\t");
- p++;
+ unescape = (char *) "\f";
break;
case 'u':
- if (p > pe - 4) {
+ if (pe > stringEnd - 4) {
return Qnil;
} else {
- p = JSON_convert_UTF16_to_UTF8(result, p, pe, strictConversion);
+ char buf[4];
+ 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[0] == '\\' && pe[1] == 'u') {
+ UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
+ ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
+ | (sur & 0x3FF));
+ pe += 5;
+ } else {
+ unescape = (char *) "?";
+ break;
+ }
+ }
+ unescape_len = convert_UTF32_to_UTF8(buf, ch);
+ unescape = buf;
}
break;
default:
- rb_str_buf_cat(result, p, 1);
- p++;
- break;
+ p = pe;
+ continue;
}
+ rb_str_buf_cat(result, unescape, unescape_len);
+ p = ++pe;
} else {
- char *q = p;
- while (*q != '\\' && q < pe) q++;
- rb_str_buf_cat(result, p, q - p);
- p = q;
+ pe++;
}
}
+ rb_str_buf_cat(result, p, pe - p);
return result;
}
-#line 1316 "parser.c"
+#line 1350 "parser.c"
static const int JSON_string_start = 1;
static const int JSON_string_first_final = 8;
static const int JSON_string_error = 0;
@@ -1320,24 +1354,24 @@ static const int JSON_string_error = 0;
static const int JSON_string_en_main = 1;
-#line 433 "parser.rl"
+#line 467 "parser.rl"
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
int cs = EVIL;
- *result = rb_str_new("", 0);
+ *result = rb_str_buf_new(0);
-#line 1333 "parser.c"
+#line 1367 "parser.c"
{
cs = JSON_string_start;
}
-#line 441 "parser.rl"
+#line 475 "parser.rl"
json->memo = p;
-#line 1341 "parser.c"
+#line 1375 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1362,9 +1396,9 @@ case 2:
goto st0;
goto st2;
tr2:
-#line 419 "parser.rl"
+#line 453 "parser.rl"
{
- *result = json_string_unescape(json->memo + 1, p);
+ *result = json_string_unescape(*result, json->memo + 1, p);
if (NIL_P(*result)) {
p--;
{p++; cs = 8; goto _out;}
@@ -1373,14 +1407,14 @@ tr2:
{p = (( p + 1))-1;}
}
}
-#line 430 "parser.rl"
+#line 464 "parser.rl"
{ p--; {p++; cs = 8; goto _out;} }
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
-#line 1384 "parser.c"
+#line 1418 "parser.c"
goto st0;
st3:
if ( ++p == pe )
@@ -1456,7 +1490,7 @@ case 7:
_out: {}
}
-#line 443 "parser.rl"
+#line 477 "parser.rl"
if (cs >= JSON_string_first_final) {
return p + 1;
@@ -1467,7 +1501,7 @@ case 7:
-#line 1471 "parser.c"
+#line 1505 "parser.c"
static const int JSON_start = 1;
static const int JSON_first_final = 10;
static const int JSON_error = 0;
@@ -1475,7 +1509,7 @@ static const int JSON_error = 0;
static const int JSON_en_main = 1;
-#line 477 "parser.rl"
+#line 511 "parser.rl"
/*
@@ -1490,7 +1524,7 @@ static const int JSON_en_main = 1;
*
*/
-inline static VALUE convert_encoding(VALUE source)
+static VALUE convert_encoding(VALUE source)
{
char *ptr = RSTRING_PTR(source);
long len = RSTRING_LEN(source);
@@ -1500,28 +1534,28 @@ inline static VALUE convert_encoding(VALUE source)
#ifdef HAVE_RUBY_ENCODING_H
{
VALUE encoding = rb_funcall(source, i_encoding, 0);
- if (encoding == mEncoding_ASCII_8BIT) {
+ if (encoding == CEncoding_ASCII_8BIT) {
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_32BE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32BE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_16BE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16BE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_32LE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32LE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_16LE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16LE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else {
- source = rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_8);
+ FORCE_UTF8(source);
}
} else {
- source = rb_funcall(source, i_encode, 1, mEncoding_UTF_8);
+ source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8);
}
}
#else
@@ -1566,7 +1600,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
char *ptr;
long len;
VALUE source, opts;
- GET_STRUCT;
+ GET_PARSER;
rb_scan_args(argc, argv, "11", &source, &opts);
source = convert_encoding(StringValue(source));
ptr = RSTRING_PTR(source);
@@ -1644,19 +1678,19 @@ static VALUE cParser_parse(VALUE self)
char *p, *pe;
int cs = EVIL;
VALUE result = Qnil;
- GET_STRUCT;
+ GET_PARSER;
-#line 1651 "parser.c"
+#line 1685 "parser.c"
{
cs = JSON_start;
}
-#line 648 "parser.rl"
+#line 682 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1660 "parser.c"
+#line 1694 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1712,7 +1746,7 @@ case 5:
goto st1;
goto st5;
tr3:
-#line 466 "parser.rl"
+#line 500 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1721,7 +1755,7 @@ tr3:
}
goto st10;
tr4:
-#line 459 "parser.rl"
+#line 493 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1733,7 +1767,7 @@ st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 1737 "parser.c"
+#line 1771 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -1790,7 +1824,7 @@ case 9:
_out: {}
}
-#line 651 "parser.rl"
+#line 685 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
@@ -1799,7 +1833,7 @@ case 9:
}
}
-inline static JSON_Parser *JSON_allocate()
+static JSON_Parser *JSON_allocate()
{
JSON_Parser *json = ALLOC(JSON_Parser);
MEMZERO(json, JSON_Parser, 1);
@@ -1833,7 +1867,7 @@ static VALUE cJSON_parser_s_allocate(VALUE klass)
*/
static VALUE cParser_source(VALUE self)
{
- GET_STRUCT;
+ GET_PARSER;
return rb_str_dup(json->Vsource);
}
@@ -1864,12 +1898,12 @@ void Init_parser()
i_object_class = rb_intern("object_class");
i_array_class = rb_intern("array_class");
#ifdef HAVE_RUBY_ENCODING_H
- mEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
- mEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be"));
- mEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le"));
- mEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be"));
- mEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le"));
- mEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit"));
+ 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");
i_encode_bang = rb_intern("encode!");
diff --git a/ext/json/ext/parser.h b/ext/json/ext/parser.h
new file mode 100644
index 0000000..5f2506d
--- /dev/null
+++ b/ext/json/ext/parser.h
@@ -0,0 +1,79 @@
+#ifndef _PARSER_H_
+#define _PARSER_H_
+
+#include "ruby.h"
+
+#if HAVE_RE_H
+#include "re.h"
+#endif
+
+#if HAVE_RUBY_ST_H
+#include "ruby/st.h"
+#endif
+
+#if HAVE_ST_H
+#include "st.h"
+#endif
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
+#else
+#define FORCE_UTF8(obj)
+#endif
+
+#ifndef RHASH_TBL
+#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
+#endif
+
+/* unicode */
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+typedef struct JSON_ParserStruct {
+ VALUE Vsource;
+ char *source;
+ long len;
+ char *memo;
+ VALUE create_id;
+ int max_nesting;
+ int current_nesting;
+ int allow_nan;
+ VALUE object_class;
+ VALUE array_class;
+} JSON_Parser;
+
+#define GET_PARSER \
+ JSON_Parser *json; \
+ Data_Get_Struct(self, JSON_Parser, json)
+
+#define MinusInfinity "-Infinity"
+#define EVIL 0x666
+
+static UTF32 unescape_unicode(const unsigned char *p);
+static int convert_UTF32_to_UTF8(char *buf, UTF32 ch);
+static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result);
+static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
+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 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);
+static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self);
+static VALUE cParser_parse(VALUE self);
+static JSON_Parser *JSON_allocate();
+static void JSON_mark(JSON_Parser *json);
+static void JSON_free(JSON_Parser *json);
+static VALUE cJSON_parser_s_allocate(VALUE klass);
+static VALUE cParser_source(VALUE self);
+
+#endif
diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser.rl
index 7de7bb1..b91ac00 100644
--- a/ext/json/ext/parser/parser.rl
+++ b/ext/json/ext/parser.rl
@@ -1,29 +1,75 @@
-#include "ruby.h"
-#include "unicode.h"
-#if HAVE_RE_H
-#include "re.h"
-#endif
-#if HAVE_RUBY_ST_H
-#include "ruby/st.h"
-#endif
-#if HAVE_ST_H
-#include "st.h"
-#endif
+#include "parser.h"
+
+/* unicode */
+
+static const char digit_values[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+};
+
+static UTF32 unescape_unicode(const unsigned char *p)
+{
+ char b;
+ UTF32 result = 0;
+ b = digit_values[p[0]];
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ result = (result << 4) | b;
+ b = digit_values[p[1]];
+ result = (result << 4) | b;
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ b = digit_values[p[2]];
+ result = (result << 4) | b;
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ b = digit_values[p[3]];
+ result = (result << 4) | b;
+ if (b < 0) return UNI_REPLACEMENT_CHAR;
+ return result;
+}
-#define EVIL 0x666
+static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
+{
+ int len = 1;
+ if (ch <= 0x7F) {
+ buf[0] = (char) ch;
+ } else if (ch <= 0x07FF) {
+ buf[0] = (char) ((ch >> 6) | 0xC0);
+ buf[1] = (char) ((ch & 0x3F) | 0x80);
+ len++;
+ } else if (ch <= 0xFFFF) {
+ buf[0] = (char) ((ch >> 12) | 0xE0);
+ buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[2] = (char) ((ch & 0x3F) | 0x80);
+ len += 2;
+ } else if (ch <= 0x1fffff) {
+ buf[0] =(char) ((ch >> 18) | 0xF0);
+ buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
+ buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[3] =(char) ((ch & 0x3F) | 0x80);
+ len += 3;
+ } else {
+ buf[0] = '?';
+ }
+ return len;
+}
-#ifndef RHASH_TBL
-#define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
-#endif
#ifdef HAVE_RUBY_ENCODING_H
-#include "ruby/encoding.h"
-#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
-static VALUE mEncoding_ASCII_8BIT, mEncoding_UTF_8, mEncoding_UTF_16BE,
- mEncoding_UTF_16LE, mEncoding_UTF_32BE, mEncoding_UTF_32LE;
+static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8, CEncoding_UTF_16BE,
+ CEncoding_UTF_16LE, CEncoding_UTF_32BE, CEncoding_UTF_32LE;
static ID i_encoding, i_encode, i_encode_bang, i_force_encoding;
#else
-#define FORCE_UTF8(obj)
static ID i_iconv;
#endif
@@ -33,32 +79,6 @@ 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_object_class, i_array_class;
-#define MinusInfinity "-Infinity"
-
-typedef struct JSON_ParserStruct {
- VALUE Vsource;
- char *source;
- long len;
- char *memo;
- VALUE create_id;
- int max_nesting;
- int current_nesting;
- int allow_nan;
- VALUE object_class;
- VALUE array_class;
-} JSON_Parser;
-
-static char *JSON_parse_object(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_value(JSON_Parser *json, char *p, char *pe, VALUE *result);
-static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result);
-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);
-
-#define GET_STRUCT \
- JSON_Parser *json; \
- Data_Get_Struct(self, JSON_Parser, json);
-
%%{
machine JSON_common;
@@ -354,59 +374,73 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
}
}
-static VALUE json_string_unescape(char *p, char *pe)
+static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
{
- VALUE result = rb_str_buf_new(pe - p + 1);
-
- while (p < pe) {
- if (*p == '\\') {
- p++;
- if (p >= pe) return Qnil; /* raise an exception later, \ at end */
- switch (*p) {
+ char *p = string, *pe = string, *unescape;
+ int unescape_len;
+
+ while (pe < stringEnd) {
+ if (*pe == '\\') {
+ unescape = (char *) "?";
+ unescape_len = 1;
+ if (pe > p) rb_str_buf_cat(result, p, pe - p);
+ switch (*++pe) {
+ case 'n':
+ unescape = (char *) "\n";
+ break;
+ case 'r':
+ unescape = (char *) "\r";
+ break;
+ case 't':
+ unescape = (char *) "\t";
+ break;
case '"':
+ unescape = (char *) "\"";
+ break;
case '\\':
- rb_str_buf_cat(result, p, 1);
- p++;
+ unescape = (char *) "\\";
break;
case 'b':
- rb_str_buf_cat2(result, "\b");
- p++;
+ unescape = (char *) "\b";
break;
case 'f':
- rb_str_buf_cat2(result, "\f");
- p++;
- break;
- case 'n':
- rb_str_buf_cat2(result, "\n");
- p++;
- break;
- case 'r':
- rb_str_buf_cat2(result, "\r");
- p++;
- break;
- case 't':
- rb_str_buf_cat2(result, "\t");
- p++;
+ unescape = (char *) "\f";
break;
case 'u':
- if (p > pe - 4) {
+ if (pe > stringEnd - 4) {
return Qnil;
} else {
- p = JSON_convert_UTF16_to_UTF8(result, p, pe, strictConversion);
+ char buf[4];
+ 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[0] == '\\' && pe[1] == 'u') {
+ UTF32 sur = unescape_unicode((unsigned char *) pe + 2);
+ ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
+ | (sur & 0x3FF));
+ pe += 5;
+ } else {
+ unescape = (char *) "?";
+ break;
+ }
+ }
+ unescape_len = convert_UTF32_to_UTF8(buf, ch);
+ unescape = buf;
}
break;
default:
- rb_str_buf_cat(result, p, 1);
- p++;
- break;
+ p = pe;
+ continue;
}
+ rb_str_buf_cat(result, unescape, unescape_len);
+ p = ++pe;
} else {
- char *q = p;
- while (*q != '\\' && q < pe) q++;
- rb_str_buf_cat(result, p, q - p);
- p = q;
+ pe++;
}
}
+ rb_str_buf_cat(result, p, pe - p);
return result;
}
@@ -417,7 +451,7 @@ static VALUE json_string_unescape(char *p, char *pe)
write data;
action parse_string {
- *result = json_string_unescape(json->memo + 1, p);
+ *result = json_string_unescape(*result, json->memo + 1, p);
if (NIL_P(*result)) {
fhold;
fbreak;
@@ -436,7 +470,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
{
int cs = EVIL;
- *result = rb_str_new("", 0);
+ *result = rb_str_buf_new(0);
%% write init;
json->memo = p;
%% write exec;
@@ -488,7 +522,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
*
*/
-inline static VALUE convert_encoding(VALUE source)
+static VALUE convert_encoding(VALUE source)
{
char *ptr = RSTRING_PTR(source);
long len = RSTRING_LEN(source);
@@ -498,28 +532,28 @@ inline static VALUE convert_encoding(VALUE source)
#ifdef HAVE_RUBY_ENCODING_H
{
VALUE encoding = rb_funcall(source, i_encoding, 0);
- if (encoding == mEncoding_ASCII_8BIT) {
+ if (encoding == CEncoding_ASCII_8BIT) {
if (len >= 4 && ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_32BE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32BE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else if (len >= 4 && ptr[0] == 0 && ptr[2] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_16BE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16BE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else if (len >= 4 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_32LE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_32LE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else if (len >= 4 && ptr[1] == 0 && ptr[3] == 0) {
source = rb_str_dup(source);
- rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_16LE);
- source = rb_funcall(source, i_encode_bang, 1, mEncoding_UTF_8);
+ rb_funcall(source, i_force_encoding, 1, CEncoding_UTF_16LE);
+ source = rb_funcall(source, i_encode_bang, 1, CEncoding_UTF_8);
} else {
- source = rb_funcall(source, i_force_encoding, 1, mEncoding_UTF_8);
+ FORCE_UTF8(source);
}
} else {
- source = rb_funcall(source, i_encode, 1, mEncoding_UTF_8);
+ source = rb_funcall(source, i_encode, 1, CEncoding_UTF_8);
}
}
#else
@@ -564,7 +598,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
char *ptr;
long len;
VALUE source, opts;
- GET_STRUCT;
+ GET_PARSER;
rb_scan_args(argc, argv, "11", &source, &opts);
source = convert_encoding(StringValue(source));
ptr = RSTRING_PTR(source);
@@ -642,7 +676,7 @@ static VALUE cParser_parse(VALUE self)
char *p, *pe;
int cs = EVIL;
VALUE result = Qnil;
- GET_STRUCT;
+ GET_PARSER;
%% write init;
p = json->source;
@@ -656,7 +690,7 @@ static VALUE cParser_parse(VALUE self)
}
}
-inline static JSON_Parser *JSON_allocate()
+static JSON_Parser *JSON_allocate()
{
JSON_Parser *json = ALLOC(JSON_Parser);
MEMZERO(json, JSON_Parser, 1);
@@ -690,7 +724,7 @@ static VALUE cJSON_parser_s_allocate(VALUE klass)
*/
static VALUE cParser_source(VALUE self)
{
- GET_STRUCT;
+ GET_PARSER;
return rb_str_dup(json->Vsource);
}
@@ -721,12 +755,12 @@ void Init_parser()
i_object_class = rb_intern("object_class");
i_array_class = rb_intern("array_class");
#ifdef HAVE_RUBY_ENCODING_H
- mEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
- mEncoding_UTF_16BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16be"));
- mEncoding_UTF_16LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-16le"));
- mEncoding_UTF_32BE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32be"));
- mEncoding_UTF_32LE = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-32le"));
- mEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit"));
+ 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");
i_encode_bang = rb_intern("encode!");
diff --git a/ext/json/ext/parser/unicode.c b/ext/json/ext/parser/unicode.c
deleted file mode 100644
index 6bd29e2..0000000
--- a/ext/json/ext/parser/unicode.c
+++ /dev/null
@@ -1,154 +0,0 @@
-#include "unicode.h"
-
-/*
- * Copyright 2001-2004 Unicode, Inc.
- *
- * Disclaimer
- *
- * This source code is provided as is by Unicode, Inc. No claims are
- * made as to fitness for any particular purpose. No warranties of any
- * kind are expressed or implied. The recipient agrees to determine
- * applicability of information provided. If this file has been
- * purchased on magnetic or optical media from Unicode, Inc., the
- * sole remedy for any claim will be exchange of defective media
- * within 90 days of receipt.
- *
- * Limitations on Rights to Redistribute This Code
- *
- * Unicode, Inc. hereby grants the right to freely use the information
- * supplied in this file in the creation of products supporting the
- * Unicode Standard, and to make copies of this file in any form
- * for internal or external distribution as long as this notice
- * remains attached.
- */
-
-/*
- * Index into the table below with the first byte of a UTF-8 sequence to
- * get the number of trailing bytes that are supposed to follow it.
- * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
- * left as-is for anyone who may want to do such conversion, which was
- * allowed in earlier algorithms.
- */
-static const char trailingBytesForUTF8[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
-};
-
-/*
- * Magic values subtracted from a buffer value during UTF8 conversion.
- * This table contains as many values as there might be trailing bytes
- * in a UTF-8 sequence.
- */
-static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
- 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
-
-/*
- * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
- * into the first byte, depending on how many bytes follow. There are
- * as many entries in this table as there are UTF-8 sequence types.
- * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
- * for *legal* UTF-8 will be 4 or fewer bytes total.
- */
-static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
-
-char *JSON_convert_UTF16_to_UTF8 (
- VALUE buffer,
- char *source,
- char *sourceEnd,
- ConversionFlags flags)
-{
- UTF16 *tmp, *tmpPtr, *tmpEnd;
- char buf[5];
- long n = 0, i;
- char *p = source - 1;
-
- while (p < sourceEnd && p[0] == '\\' && p[1] == 'u') {
- p += 6;
- n++;
- }
- p = source + 1;
- buf[4] = 0;
- tmpPtr = tmp = ALLOC_N(UTF16, n);
- tmpEnd = tmp + n;
- for (i = 0; i < n; i++) {
- buf[0] = *p++;
- buf[1] = *p++;
- buf[2] = *p++;
- buf[3] = *p++;
- tmpPtr[i] = (UTF16)strtol(buf, NULL, 16);
- p += 2;
- }
-
- while (tmpPtr < tmpEnd) {
- UTF32 ch;
- unsigned short bytesToWrite = 0;
- const UTF32 byteMask = 0xBF;
- const UTF32 byteMark = 0x80;
- ch = *tmpPtr++;
- /* If we have a surrogate pair, convert to UTF32 first. */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
- /* If the 16 bits following the high surrogate are in the source
- * buffer... */
- if (tmpPtr < tmpEnd) {
- UTF32 ch2 = *tmpPtr;
- /* If it's a low surrogate, convert to UTF32. */
- if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
- ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
- + (ch2 - UNI_SUR_LOW_START) + halfBase;
- ++tmpPtr;
- } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
- ruby_xfree(tmp);
- rb_raise(rb_path2class("JSON::ParserError"),
- "\\uXXXX is illegal/malformed utf-16 near %s", source);
- }
- } else { /* We don't have the 16 bits following the high surrogate. */
- ruby_xfree(tmp);
- rb_raise(rb_path2class("JSON::ParserError"),
- "partial character in source, but hit end near %s", source);
- break;
- }
- } else if (flags == strictConversion) {
- /* UTF-16 surrogate values are illegal in UTF-32 */
- if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
- ruby_xfree(tmp);
- rb_raise(rb_path2class("JSON::ParserError"),
- "\\uXXXX is illegal/malformed utf-16 near %s", source);
- }
- }
- /* Figure out how many bytes the result will require */
- if (ch < (UTF32) 0x80) {
- bytesToWrite = 1;
- } else if (ch < (UTF32) 0x800) {
- bytesToWrite = 2;
- } else if (ch < (UTF32) 0x10000) {
- bytesToWrite = 3;
- } else if (ch < (UTF32) 0x110000) {
- bytesToWrite = 4;
- } else {
- bytesToWrite = 3;
- ch = UNI_REPLACEMENT_CHAR;
- }
-
- buf[0] = 0;
- buf[1] = 0;
- buf[2] = 0;
- buf[3] = 0;
- p = buf + bytesToWrite;
- switch (bytesToWrite) { /* note: everything falls through. */
- case 4: *--p = (UTF8) ((ch | byteMark) & byteMask); ch >>= 6;
- case 3: *--p = (UTF8) ((ch | byteMark) & byteMask); ch >>= 6;
- case 2: *--p = (UTF8) ((ch | byteMark) & byteMask); ch >>= 6;
- case 1: *--p = (UTF8) (ch | firstByteMark[bytesToWrite]);
- }
- rb_str_buf_cat(buffer, p, bytesToWrite);
- }
- ruby_xfree(tmp);
- source += 5 + (n - 1) * 6;
- return source;
-}
diff --git a/ext/json/ext/parser/unicode.h b/ext/json/ext/parser/unicode.h
deleted file mode 100644
index 155da0c..0000000
--- a/ext/json/ext/parser/unicode.h
+++ /dev/null
@@ -1,58 +0,0 @@
-
-#ifndef _PARSER_UNICODE_H_
-#define _PARSER_UNICODE_H_
-
-#include "ruby.h"
-
-typedef unsigned long UTF32; /* at least 32 bits */
-typedef unsigned short UTF16; /* at least 16 bits */
-typedef unsigned char UTF8; /* typically 8 bits */
-
-#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
-#define UNI_MAX_BMP (UTF32)0x0000FFFF
-#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
-#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
-#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
-
-#define UNI_SUR_HIGH_START (UTF32)0xD800
-#define UNI_SUR_HIGH_END (UTF32)0xDBFF
-#define UNI_SUR_LOW_START (UTF32)0xDC00
-#define UNI_SUR_LOW_END (UTF32)0xDFFF
-
-static const int halfShift = 10; /* used for shifting by 10 bits */
-
-static const UTF32 halfBase = 0x0010000UL;
-static const UTF32 halfMask = 0x3FFUL;
-
-typedef enum {
- conversionOK = 0, /* conversion successful */
- sourceExhausted, /* partial character in source, but hit end */
- targetExhausted, /* insuff. room in target for conversion */
- sourceIllegal /* source sequence is illegal/malformed */
-} ConversionResult;
-
-typedef enum {
- strictConversion = 0,
- lenientConversion
-} ConversionFlags;
-
-char *JSON_convert_UTF16_to_UTF8 (
- VALUE buffer,
- char *source,
- char *sourceEnd,
- ConversionFlags flags);
-
-#ifndef RARRAY_PTR
-#define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
-#endif
-#ifndef RARRAY_LEN
-#define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
-#endif
-#ifndef RSTRING_PTR
-#define RSTRING_PTR(string) RSTRING(string)->ptr
-#endif
-#ifndef RSTRING_LEN
-#define RSTRING_LEN(string) RSTRING(string)->len
-#endif
-
-#endif
diff --git a/lib/json/common.rb b/lib/json/common.rb
index 39f6336..5c10071 100644
--- a/lib/json/common.rb
+++ b/lib/json/common.rb
@@ -91,15 +91,15 @@ module JSON
# deep.
class NestingError < ParserError; end
+ # :stopdoc:
+ class CircularDatastructure < NestingError; end
+ # :startdoc:
+
# This exception is raised, if a generator or unparser error occurs.
class GeneratorError < JSONError; end
# For backwards compatibility
UnparserError = GeneratorError
- # If a circular data structure is encountered while unparsing
- # this exception is raised.
- class CircularDatastructure < GeneratorError; end
-
# This exception is raised, if the required unicode support is missing on the
# system. Usually this means, that the iconv library is not installed.
class MissingUnicodeSupport < JSONError; end
@@ -162,8 +162,6 @@ module JSON
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
- # * *check_circular*: true if checking for circular data structures
- # should be done (the default), false otherwise.
# * *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.
@@ -180,11 +178,7 @@ module JSON
else
state = State.new
end
- result = obj.to_json(state)
- if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
- raise GeneratorError, "only generation of JSON objects or arrays allowed"
- end
- result
+ state.generate(obj)
end
# :stopdoc:
@@ -199,12 +193,24 @@ module JSON
#
# *WARNING*: Be careful not to pass any Ruby data structures with circles as
# _obj_ argument, because this will cause JSON to go into an infinite loop.
- def fast_generate(obj)
- result = obj.to_json(nil)
- if result !~ /\A(?:\[.*\]|\{.*\})\Z/
- raise GeneratorError, "only generation of JSON objects or arrays allowed"
+ def fast_generate(obj, opts = nil)
+ state = JSON.state.new(
+ :indent => '',
+ :space => '',
+ :object_nl => "",
+ :array_nl => ""
+ )
+ if opts
+ if opts.respond_to? :to_hash
+ opts = opts.to_hash
+ elsif opts.respond_to? :to_h
+ opts = opts.to_h
+ else
+ raise TypeError, "can't convert #{opts.class} into Hash"
+ end
+ state.configure(opts)
end
- result
+ state.generate(obj)
end
# :stopdoc:
@@ -221,11 +227,10 @@ module JSON
# generate method for a more detailed explanation.
def pretty_generate(obj, opts = nil)
state = JSON.state.new(
- :indent => ' ',
- :space => ' ',
- :object_nl => "\n",
- :array_nl => "\n",
- :check_circular => true
+ :indent => ' ',
+ :space => ' ',
+ :object_nl => "\n",
+ :array_nl => "\n"
)
if opts
if opts.respond_to? :to_hash
@@ -237,11 +242,7 @@ module JSON
end
state.configure(opts)
end
- result = obj.to_json(state)
- if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
- raise GeneratorError, "only generation of JSON objects or arrays allowed"
- end
- result
+ state.generate(obj)
end
# :stopdoc:
diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb
index 57ef483..256bdb5 100644
--- a/lib/json/pure/generator.rb
+++ b/lib/json/pure/generator.rb
@@ -44,6 +44,15 @@ module JSON
string << '' # XXX workaround: avoid buffer sharing
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 << '' # XXX workaround: avoid buffer sharing
+ string.force_encoding(::Encoding::ASCII_8BIT)
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
string.gsub!(/(
(?:
[\xc2-\xdf][\x80-\xbf] |
@@ -63,6 +72,10 @@ module JSON
end
else
def utf8_to_json(string) # :nodoc:
+ string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
+ end
+
+ def utf8_to_json_ascii(string) # :nodoc:
string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
string.gsub!(/(
(?:
@@ -81,7 +94,7 @@ module JSON
raise GeneratorError, "Caught #{e.class}: #{e}"
end
end
- module_function :utf8_to_json
+ module_function :utf8_to_json, :utf8_to_json_ascii
module Pure
module Generator
@@ -112,22 +125,20 @@ module JSON
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
- # * *check_circular*: true if checking for circular data structures
- # should be done (the default), false otherwise.
- # * *check_circular*: true if checking for circular data structures
- # should be done, false (the default) otherwise.
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
+ # * *max_nesting*: sets the maximum level of data structure nesting in
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
# * *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.
def initialize(opts = {})
- @seen = {}
@indent = ''
@space = ''
@space_before = ''
@object_nl = ''
@array_nl = ''
- @check_circular = true
@allow_nan = false
+ @ascii_only = false
configure opts
end
@@ -159,10 +170,10 @@ module JSON
raise NestingError, "nesting of #{current_nesting} is too deep"
end
- # Returns true, if circular data structures should be checked,
+ # Returns true, if circular data structures are checked,
# otherwise returns false.
def check_circular?
- @check_circular
+ !!@max_nesting.zero?
end
# Returns true if NaN, Infinity, and -Infinity should be considered as
@@ -171,21 +182,8 @@ module JSON
@allow_nan
end
- # Returns _true_, if _object_ was already seen during this generating
- # run.
- def seen?(object)
- @seen.key?(object.__id__)
- end
-
- # Remember _object_, to find out if it was already encountered (if a
- # cyclic data structure is if a cyclic data structure is rendered).
- def remember(object)
- @seen[object.__id__] = true
- end
-
- # Forget _object_ for this generating run.
- def forget(object)
- @seen.delete object.__id__
+ def ascii_only?
+ @ascii_only
end
# Configure this State instance with the Hash _opts_, and return
@@ -196,8 +194,8 @@ module JSON
@space_before = opts[:space_before] if opts.key?(:space_before)
@object_nl = opts[:object_nl] if opts.key?(:object_nl)
@array_nl = opts[:array_nl] if opts.key?(:array_nl)
- @check_circular = !!opts[:check_circular] if opts.key?(:check_circular)
@allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
if !opts.key?(:max_nesting) # defaults to 19
@max_nesting = 19
elsif opts[:max_nesting]
@@ -217,6 +215,17 @@ module JSON
end
result
end
+
+ # 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)
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
+ end
+ result
+ end
end
module GeneratorMethods
@@ -237,25 +246,12 @@ module JSON
if state
state = JSON.state.from_state(state)
state.check_max_nesting(depth)
- json_check_circular(state) { json_transform(state, depth) }
- else
- json_transform(state, depth)
end
+ json_transform(state, depth)
end
private
- def json_check_circular(state)
- if state and state.check_circular?
- state.seen?(self) and raise JSON::CircularDatastructure,
- "circular data structures not supported!"
- state.remember self
- end
- yield
- ensure
- state and state.forget self
- end
-
def json_shift(state, depth)
state and not state.object_nl.empty? or return ''
state.indent * depth
@@ -267,16 +263,22 @@ module JSON
delim << state.object_nl
result = '{'
result << state.object_nl
- result << map { |key,value|
- s = json_shift(state, depth + 1)
- s << key.to_s.to_json(state, depth + 1)
- s << state.space_before
- s << ':'
- s << state.space
- s << value.to_json(state, depth + 1)
- }.join(delim)
+ depth += 1
+ first = true
+ indent = state && !state.object_nl.empty?
+ each { |key,value|
+ result << delim unless first
+ result << state.indent * depth if indent
+ result << key.to_s.to_json(state, depth)
+ result << state.space_before
+ result << ':'
+ result << state.space
+ result << value.to_json(state, depth)
+ first = false
+ }
+ depth -= 1
result << state.object_nl
- result << json_shift(state, depth)
+ result << state.indent * depth if indent if indent
result << '}'
else
result = '{'
@@ -299,41 +301,30 @@ module JSON
if state
state = JSON.state.from_state(state)
state.check_max_nesting(depth)
- json_check_circular(state) { json_transform(state, depth) }
- else
- json_transform(state, depth)
end
+ json_transform(state, depth)
end
private
- def json_check_circular(state)
- if state and state.check_circular?
- state.seen?(self) and raise JSON::CircularDatastructure,
- "circular data structures not supported!"
- state.remember self
- end
- yield
- ensure
- state and state.forget self
- end
-
- def json_shift(state, depth)
- state and not state.array_nl.empty? or return ''
- state.indent * depth
- end
-
def json_transform(state, depth)
delim = ','
if state
delim << state.array_nl
result = '['
result << state.array_nl
- result << map { |value|
- json_shift(state, depth + 1) << value.to_json(state, depth + 1)
- }.join(delim)
+ depth += 1
+ first = true
+ indent = state && !state.array_nl.empty?
+ each { |value|
+ result << delim unless first
+ result << state.indent * depth if indent
+ result << value.to_json(state, depth)
+ first = false
+ }
+ depth -= 1
result << state.array_nl
- result << json_shift(state, depth)
+ result << state.indent * depth if indent
result << ']'
else
'[' << map { |value| value.to_json }.join(delim) << ']'
@@ -373,11 +364,17 @@ module JSON
# This string should be encoded with UTF-8 A call to this method
# returns a JSON string encoded with UTF16 big endian characters as
# \u????.
- def to_json(*)
+ def to_json(*args)
+ state, = *args
+ state ||= JSON.state.from_state(state)
if encoding == ::Encoding::UTF_8
- '"' << JSON.utf8_to_json(self) << '"'
+ 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
end
@@ -385,16 +382,23 @@ module JSON
# This string should be encoded with UTF-8 A call to this method
# returns a JSON string encoded with UTF16 big endian characters as
# \u????.
- def to_json(*)
- '"' << JSON.utf8_to_json(self) << '"'
+ def to_json(*args)
+ state, = *args
+ state ||= JSON.state.from_state(state)
+ if state.ascii_only?
+ '"' << JSON.utf8_to_json_ascii(self) << '"'
+ else
+ '"' << JSON.utf8_to_json(self) << '"'
+ end
end
end
# Module that holds the extinding methods if, the String module is
# included.
module Extend
- # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
- # key "raw"). The Ruby String can be created by this module method.
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
+ # array for the key "raw"). The Ruby String can be created by this
+ # module method.
def json_create(o)
o['raw'].pack('C*')
end
diff --git a/tests/test_json_encoding.rb b/tests/test_json_encoding.rb
index bfb3e60..fdea329 100644
--- a/tests/test_json_encoding.rb
+++ b/tests/test_json_encoding.rb
@@ -57,11 +57,12 @@ class TC_JSONEncoding < Test::Unit::TestCase
end
def test_generate
- assert_equal @generated, JSON.generate(@parsed)
+ assert_equal @generated, JSON.generate(@parsed, :ascii_only => true)
if defined?(::Encoding)
- assert_equal @generated, JSON.generate(@utf_16_data)
+ assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true)
else
- assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data) }
+ # 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_generate.rb b/tests/test_json_generate.rb
index e725e6f..e72c562 100755
--- a/tests/test_json_generate.rb
+++ b/tests/test_json_generate.rb
@@ -91,13 +91,13 @@ EOT
#assert s.check_circular
h = { 1=>2 }
h[3] = h
- assert_raises(JSON::CircularDatastructure) { generate(h) }
- assert_raises(JSON::CircularDatastructure) { generate(h, s) }
+ assert_raises(JSON::NestingError) { generate(h) }
+ assert_raises(JSON::NestingError) { generate(h, s) }
s = JSON.state.new(:check_circular => true)
#assert s.check_circular
a = [ 1, 2 ]
a << a
- assert_raises(JSON::CircularDatastructure) { generate(a, s) }
+ assert_raises(JSON::NestingError) { generate(a, s) }
end
def test_allow_nan
diff --git a/tests/test_json_unicode.rb b/tests/test_json_unicode.rb
index 1454fe1..505f5d5 100755
--- a/tests/test_json_unicode.rb
+++ b/tests/test_json_unicode.rb
@@ -19,22 +19,36 @@ class TC_JSONUnicode < Test::Unit::TestCase
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
+ 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
+ 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
+ assert_equal json, utf8.to_json(:ascii_only => true)
assert_equal utf8, parse(json)
- assert_equal '["\\u00c3"]', JSON.generate(["Ã"])
+ 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)
+ assert_equal json, JSON.generate(utf8, :ascii_only => true)
assert_equal utf8, JSON.parse(json)
end
@@ -55,7 +69,7 @@ class TC_JSONUnicode < Test::Unit::TestCase
end
end
assert_raise(JSON::GeneratorError) do
- JSON.generate(["\x80"])
+ JSON.generate(["\x80"], :ascii_only => true)
end
assert_equal "\302\200", JSON.parse('["\u0080"]').first
end