oj 3.12.0 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/ext/oj/buf.h +9 -0
- data/ext/oj/cache.c +187 -0
- data/ext/oj/cache.h +20 -0
- data/ext/oj/compat.c +10 -16
- data/ext/oj/custom.c +13 -12
- data/ext/oj/debug.c +131 -0
- data/ext/oj/dump.c +49 -52
- data/ext/oj/dump_compat.c +3 -3
- data/ext/oj/dump_object.c +7 -7
- data/ext/oj/dump_strict.c +3 -3
- data/ext/oj/err.h +19 -0
- data/ext/oj/extconf.rb +4 -0
- data/ext/oj/hash_test.c +3 -30
- data/ext/oj/intern.c +398 -0
- data/ext/oj/intern.h +27 -0
- data/ext/oj/mimic_json.c +9 -9
- data/ext/oj/object.c +10 -58
- data/ext/oj/odd.c +1 -1
- data/ext/oj/oj.c +172 -107
- data/ext/oj/oj.h +2 -2
- data/ext/oj/parse.c +4 -4
- data/ext/oj/parser.c +1527 -0
- data/ext/oj/parser.h +90 -0
- data/ext/oj/rails.c +4 -4
- data/ext/oj/resolve.c +2 -20
- data/ext/oj/saj2.c +346 -0
- data/ext/oj/scp.c +1 -1
- data/ext/oj/sparse.c +1 -1
- data/ext/oj/stream_writer.c +3 -3
- data/ext/oj/strict.c +10 -27
- data/ext/oj/usual.c +1222 -0
- data/ext/oj/validate.c +50 -0
- data/ext/oj/wab.c +15 -16
- data/lib/oj/mimic.rb +2 -0
- data/lib/oj/version.rb +1 -1
- data/pages/Modes.md +2 -0
- data/pages/Options.md +23 -5
- data/pages/Parser.md +309 -0
- data/test/foo.rb +2 -9
- data/test/json_gem/json_common_interface_test.rb +1 -1
- data/test/perf_parser.rb +184 -0
- data/test/test_parser.rb +27 -0
- data/test/test_parser_saj.rb +245 -0
- data/test/test_parser_usual.rb +213 -0
- metadata +23 -5
- data/ext/oj/hash.c +0 -146
- data/ext/oj/hash.h +0 -21
data/test/foo.rb
CHANGED
@@ -7,14 +7,7 @@ $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
|
|
7
7
|
end
|
8
8
|
|
9
9
|
require 'oj'
|
10
|
-
require 'active_support'
|
11
10
|
|
12
|
-
|
11
|
+
p = Oj::Parser.new(:debug)
|
13
12
|
|
14
|
-
|
15
|
-
#Oj.mimic_JSON
|
16
|
-
begin
|
17
|
-
::JSON.load('foo=&bar')
|
18
|
-
rescue Exception => e
|
19
|
-
puts "*** #{e.class}: #{e.message}"
|
20
|
-
end
|
13
|
+
p.parse("[true, false]")
|
@@ -23,7 +23,7 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
|
|
23
23
|
'h' => 1000.0,
|
24
24
|
'i' => 0.001
|
25
25
|
}
|
26
|
-
# Tired of chasing floating point rounding and precision. Oj
|
26
|
+
# Tired of chasing floating point rounding and precision. Oj not uses the
|
27
27
|
# Ruby float parser in compat mode yet on i386 machines there are issues
|
28
28
|
# with this test when the float is included.
|
29
29
|
#@json = '{"a":2,"b":5.23683071,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
|
data/test/perf_parser.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$: << '.'
|
5
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
6
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
require 'perf'
|
10
|
+
require 'oj'
|
11
|
+
require 'json'
|
12
|
+
|
13
|
+
$verbose = false
|
14
|
+
$iter = 50_000
|
15
|
+
$with_bignum = false
|
16
|
+
$size = 1
|
17
|
+
$cache_keys = true
|
18
|
+
$symbol_keys = false
|
19
|
+
|
20
|
+
opts = OptionParser.new
|
21
|
+
opts.on("-v", "verbose") { $verbose = true }
|
22
|
+
opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
|
23
|
+
opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
|
24
|
+
opts.on("-b", "with bignum") { $with_bignum = true }
|
25
|
+
opts.on("-k", "no cache") { $cache_keys = false }
|
26
|
+
opts.on("-sym", "symbol keys") { $symbol_keys = true }
|
27
|
+
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
28
|
+
files = opts.parse(ARGV)
|
29
|
+
|
30
|
+
$obj = {
|
31
|
+
'a' => 'Alpha', # string
|
32
|
+
'b' => true, # boolean
|
33
|
+
'c' => 12345, # number
|
34
|
+
'd' => [ true, [false, [-123456789, nil], 3.9676, ['Something else.', false, 1, nil], nil]], # mix it up array
|
35
|
+
'e' => { 'zero' => nil, 'one' => 1, 'two' => 2, 'three' => [3], 'four' => [0, 1, 2, 3, 4] }, # hash
|
36
|
+
'f' => nil, # nil
|
37
|
+
'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
38
|
+
'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
|
39
|
+
}
|
40
|
+
$obj['g'] = 12345678901234567890123456789 if $with_bignum
|
41
|
+
|
42
|
+
if 0 < $size
|
43
|
+
o = $obj
|
44
|
+
$obj = []
|
45
|
+
(4 * $size).times do
|
46
|
+
$obj << o
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
$json = Oj.dump($obj)
|
51
|
+
$failed = {} # key is same as String used in tests later
|
52
|
+
Oj.default_options = {create_id: '^', create_additions: true, class_cache: true}
|
53
|
+
if $cache_keys
|
54
|
+
Oj.default_options = {cache_keys: true, cache_str: 6, symbol_keys: $symbol_keys}
|
55
|
+
else
|
56
|
+
Oj.default_options = {cache_keys: false, cache_str: 0, symbol_keys: $symbol_keys}
|
57
|
+
end
|
58
|
+
JSON.parser = JSON::Ext::Parser
|
59
|
+
|
60
|
+
class AllSaj
|
61
|
+
def initialize()
|
62
|
+
end
|
63
|
+
|
64
|
+
def hash_start(key)
|
65
|
+
end
|
66
|
+
|
67
|
+
def hash_end(key)
|
68
|
+
end
|
69
|
+
|
70
|
+
def array_start(key)
|
71
|
+
end
|
72
|
+
|
73
|
+
def array_end(key)
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_value(value, key)
|
77
|
+
end
|
78
|
+
end # AllSaj
|
79
|
+
|
80
|
+
class NoSaj
|
81
|
+
def initialize()
|
82
|
+
end
|
83
|
+
end # NoSaj
|
84
|
+
|
85
|
+
no_handler = NoSaj.new()
|
86
|
+
all_handler = AllSaj.new()
|
87
|
+
|
88
|
+
if $verbose
|
89
|
+
puts "json:\n#{$json}\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
### Validate ######################
|
93
|
+
p_val = Oj::Parser.new(:validate)
|
94
|
+
|
95
|
+
puts '-' * 80
|
96
|
+
puts "Validate Performance"
|
97
|
+
perf = Perf.new()
|
98
|
+
perf.add('Oj::Parser.validate', 'none') { p_val.parse($json) }
|
99
|
+
perf.add('Oj::Saj.none', 'none') { Oj.saj_parse(no_handler, $json) }
|
100
|
+
perf.run($iter)
|
101
|
+
|
102
|
+
### SAJ ######################
|
103
|
+
p_all = Oj::Parser.new(:saj)
|
104
|
+
p_all.handler = all_handler
|
105
|
+
p_all.cache_keys = $cache_keys
|
106
|
+
p_all.cache_strings = 6
|
107
|
+
|
108
|
+
puts '-' * 80
|
109
|
+
puts "Parse Callback Performance"
|
110
|
+
perf = Perf.new()
|
111
|
+
perf.add('Oj::Parser.saj', 'all') { p_all.parse($json) }
|
112
|
+
perf.add('Oj::Saj.all', 'all') { Oj.saj_parse(all_handler, $json) }
|
113
|
+
perf.run($iter)
|
114
|
+
|
115
|
+
### Usual ######################
|
116
|
+
p_usual = Oj::Parser.new(:usual)
|
117
|
+
p_usual.cache_keys = $cache_keys
|
118
|
+
p_usual.cache_strings = ($cache_keys ? 6 : 0)
|
119
|
+
p_usual.symbol_keys = $symbol_keys
|
120
|
+
|
121
|
+
puts '-' * 80
|
122
|
+
puts "Parse Usual Performance"
|
123
|
+
perf = Perf.new()
|
124
|
+
perf.add('Oj::Parser.usual', '') { p_usual.parse($json) }
|
125
|
+
perf.add('Oj::strict_load', '') { Oj.strict_load($json) }
|
126
|
+
perf.add('JSON::Ext', 'parse') { JSON.load($json) }
|
127
|
+
perf.run($iter)
|
128
|
+
|
129
|
+
### Usual Objects ######################
|
130
|
+
|
131
|
+
# Original Oj follows the JSON gem for creating objects which uses the class
|
132
|
+
# json_create(arg) method. Oj::Parser in usual mode supprts the same but also
|
133
|
+
# handles populating the object variables directly which is faster.
|
134
|
+
|
135
|
+
class Stuff
|
136
|
+
attr_accessor :alpha, :bravo, :charlie, :delta, :echo, :foxtrot, :golf, :hotel, :india, :juliet
|
137
|
+
def self.json_create(arg)
|
138
|
+
obj = self.new
|
139
|
+
obj.alpha = arg["alpha"]
|
140
|
+
obj.bravo = arg["bravo"]
|
141
|
+
obj.charlie = arg["charlie"]
|
142
|
+
obj.delta = arg["delta"]
|
143
|
+
obj.echo = arg["echo"]
|
144
|
+
obj.foxtrot = arg["foxtrot"]
|
145
|
+
obj.golf = arg["golf"]
|
146
|
+
obj.hotel = arg["hotel"]
|
147
|
+
obj.india = arg["india"]
|
148
|
+
obj.juliet = arg["juliet"]
|
149
|
+
obj
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
$obj_json = %|{
|
154
|
+
"alpha": [0, 1,2,3,4,5,6,7,8,9],
|
155
|
+
"bravo": true,
|
156
|
+
"charlie": 123,
|
157
|
+
"delta": "some string",
|
158
|
+
"echo": null,
|
159
|
+
"^": "Stuff",
|
160
|
+
"foxtrot": false,
|
161
|
+
"golf": "gulp",
|
162
|
+
"hotel": {"x": true, "y": false},
|
163
|
+
"india": [null, true, 123],
|
164
|
+
"juliet": "junk"
|
165
|
+
}|
|
166
|
+
|
167
|
+
p_usual.create_id = '^'
|
168
|
+
p_usual.class_cache = true
|
169
|
+
p_usual.ignore_json_create = true
|
170
|
+
|
171
|
+
JSON.create_id = '^'
|
172
|
+
|
173
|
+
puts '-' * 80
|
174
|
+
puts "Parse Usual Object Performance"
|
175
|
+
perf = Perf.new()
|
176
|
+
perf.add('Oj::Parser.usual', '') { p_usual.parse($obj_json) }
|
177
|
+
perf.add('Oj::compat_load', '') { Oj.compat_load($obj_json) }
|
178
|
+
perf.add('JSON::Ext', 'parse') { JSON.load($obj_json) }
|
179
|
+
perf.run($iter)
|
180
|
+
|
181
|
+
unless $failed.empty?
|
182
|
+
puts "The following packages were not included for the reason listed"
|
183
|
+
$failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
|
184
|
+
end
|
data/test/test_parser.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
$: << File.dirname(__FILE__)
|
5
|
+
$oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
|
6
|
+
%w(lib ext).each do |dir|
|
7
|
+
$: << File.join($oj_dir, dir)
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'minitest'
|
11
|
+
require 'minitest/autorun'
|
12
|
+
require 'stringio'
|
13
|
+
require 'date'
|
14
|
+
require 'bigdecimal'
|
15
|
+
require 'oj'
|
16
|
+
|
17
|
+
class ParserJuice < Minitest::Test
|
18
|
+
|
19
|
+
def test_array
|
20
|
+
p = Oj::Parser.new(:debug)
|
21
|
+
out = p.parse(%|[true, false, null, 123, -1.23, "abc"]|)
|
22
|
+
puts out
|
23
|
+
out = p.parse(%|{"abc": []}|)
|
24
|
+
puts out
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$: << File.dirname(__FILE__)
|
5
|
+
|
6
|
+
require 'helper'
|
7
|
+
|
8
|
+
$json = %{{
|
9
|
+
"array": [
|
10
|
+
{
|
11
|
+
"num" : 3,
|
12
|
+
"string": "message",
|
13
|
+
"hash" : {
|
14
|
+
"h2" : {
|
15
|
+
"a" : [ 1, 2, 3 ]
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
],
|
20
|
+
"boolean" : true
|
21
|
+
}}
|
22
|
+
|
23
|
+
class AllSaj < Oj::Saj
|
24
|
+
attr_accessor :calls
|
25
|
+
|
26
|
+
def initialize()
|
27
|
+
@calls = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def hash_start(key)
|
31
|
+
@calls << [:hash_start, key]
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash_end(key)
|
35
|
+
@calls << [:hash_end, key]
|
36
|
+
end
|
37
|
+
|
38
|
+
def array_start(key)
|
39
|
+
@calls << [:array_start, key]
|
40
|
+
end
|
41
|
+
|
42
|
+
def array_end(key)
|
43
|
+
@calls << [:array_end, key]
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_value(value, key)
|
47
|
+
@calls << [:add_value, value, key]
|
48
|
+
end
|
49
|
+
|
50
|
+
def error(message, line, column)
|
51
|
+
@calls << [:error, message, line, column]
|
52
|
+
end
|
53
|
+
|
54
|
+
end # AllSaj
|
55
|
+
|
56
|
+
class SajTest < Minitest::Test
|
57
|
+
|
58
|
+
def test_nil
|
59
|
+
handler = AllSaj.new()
|
60
|
+
json = %{null}
|
61
|
+
p = Oj::Parser.new(:saj)
|
62
|
+
p.handler = handler
|
63
|
+
p.parse(json)
|
64
|
+
assert_equal([[:add_value, nil, nil]], handler.calls)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_true
|
68
|
+
handler = AllSaj.new()
|
69
|
+
json = %{true}
|
70
|
+
p = Oj::Parser.new(:saj)
|
71
|
+
p.handler = handler
|
72
|
+
p.parse(json)
|
73
|
+
assert_equal([[:add_value, true, nil]], handler.calls)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_false
|
77
|
+
handler = AllSaj.new()
|
78
|
+
json = %{false}
|
79
|
+
p = Oj::Parser.new(:saj)
|
80
|
+
p.handler = handler
|
81
|
+
p.parse(json)
|
82
|
+
assert_equal([[:add_value, false, nil]], handler.calls)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_string
|
86
|
+
handler = AllSaj.new()
|
87
|
+
json = %{"a string"}
|
88
|
+
p = Oj::Parser.new(:saj)
|
89
|
+
p.handler = handler
|
90
|
+
p.parse(json)
|
91
|
+
assert_equal([[:add_value, 'a string', nil]], handler.calls)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_fixnum
|
95
|
+
handler = AllSaj.new()
|
96
|
+
json = %{12345}
|
97
|
+
p = Oj::Parser.new(:saj)
|
98
|
+
p.handler = handler
|
99
|
+
p.parse(json)
|
100
|
+
assert_equal([[:add_value, 12345, nil]], handler.calls)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_float
|
104
|
+
handler = AllSaj.new()
|
105
|
+
json = %{12345.6789}
|
106
|
+
p = Oj::Parser.new(:saj)
|
107
|
+
p.handler = handler
|
108
|
+
p.parse(json)
|
109
|
+
assert_equal([[:add_value, 12345.6789, nil]], handler.calls)
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_float_exp
|
113
|
+
handler = AllSaj.new()
|
114
|
+
json = %{12345.6789e7}
|
115
|
+
p = Oj::Parser.new(:saj)
|
116
|
+
p.handler = handler
|
117
|
+
p.parse(json)
|
118
|
+
assert_equal(1, handler.calls.size)
|
119
|
+
assert_equal(:add_value, handler.calls[0][0])
|
120
|
+
assert_equal((12345.6789e7 * 10000).to_i, (handler.calls[0][1] * 10000).to_i)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_array_empty
|
124
|
+
handler = AllSaj.new()
|
125
|
+
json = %{[]}
|
126
|
+
p = Oj::Parser.new(:saj)
|
127
|
+
p.handler = handler
|
128
|
+
p.parse(json)
|
129
|
+
assert_equal([[:array_start, nil],
|
130
|
+
[:array_end, nil]], handler.calls)
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_array
|
134
|
+
handler = AllSaj.new()
|
135
|
+
json = %{[true,false]}
|
136
|
+
p = Oj::Parser.new(:saj)
|
137
|
+
p.handler = handler
|
138
|
+
p.parse(json)
|
139
|
+
assert_equal([[:array_start, nil],
|
140
|
+
[:add_value, true, nil],
|
141
|
+
[:add_value, false, nil],
|
142
|
+
[:array_end, nil]], handler.calls)
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_hash_empty
|
146
|
+
handler = AllSaj.new()
|
147
|
+
json = %{{}}
|
148
|
+
p = Oj::Parser.new(:saj)
|
149
|
+
p.handler = handler
|
150
|
+
p.parse(json)
|
151
|
+
assert_equal([[:hash_start, nil],
|
152
|
+
[:hash_end, nil]], handler.calls)
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_hash
|
156
|
+
handler = AllSaj.new()
|
157
|
+
json = %{{"one":true,"two":false}}
|
158
|
+
p = Oj::Parser.new(:saj)
|
159
|
+
p.handler = handler
|
160
|
+
p.parse(json)
|
161
|
+
assert_equal([[:hash_start, nil],
|
162
|
+
[:add_value, true, 'one'],
|
163
|
+
[:add_value, false, 'two'],
|
164
|
+
[:hash_end, nil]], handler.calls)
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_full
|
168
|
+
handler = AllSaj.new()
|
169
|
+
Oj.saj_parse(handler, $json)
|
170
|
+
assert_equal([[:hash_start, nil],
|
171
|
+
[:array_start, 'array'],
|
172
|
+
[:hash_start, nil],
|
173
|
+
[:add_value, 3, 'num'],
|
174
|
+
[:add_value, 'message', 'string'],
|
175
|
+
[:hash_start, 'hash'],
|
176
|
+
[:hash_start, 'h2'],
|
177
|
+
[:array_start, 'a'],
|
178
|
+
[:add_value, 1, nil],
|
179
|
+
[:add_value, 2, nil],
|
180
|
+
[:add_value, 3, nil],
|
181
|
+
[:array_end, 'a'],
|
182
|
+
[:hash_end, 'h2'],
|
183
|
+
[:hash_end, 'hash'],
|
184
|
+
[:hash_end, nil],
|
185
|
+
[:array_end, 'array'],
|
186
|
+
[:add_value, true, 'boolean'],
|
187
|
+
[:hash_end, nil]], handler.calls)
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_multiple
|
191
|
+
handler = AllSaj.new()
|
192
|
+
json = %|[true][false]|
|
193
|
+
p = Oj::Parser.new(:saj)
|
194
|
+
p.handler = handler
|
195
|
+
p.parse(json)
|
196
|
+
assert_equal([
|
197
|
+
[:array_start, nil],
|
198
|
+
[:add_value, true, nil],
|
199
|
+
[:array_end, nil],
|
200
|
+
[:array_start, nil],
|
201
|
+
[:add_value, false, nil],
|
202
|
+
[:array_end, nil],
|
203
|
+
], handler.calls)
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_io
|
207
|
+
handler = AllSaj.new()
|
208
|
+
json = %| [true,false] |
|
209
|
+
p = Oj::Parser.new(:saj)
|
210
|
+
p.handler = handler
|
211
|
+
p.load(StringIO.new(json))
|
212
|
+
assert_equal([
|
213
|
+
[:array_start, nil],
|
214
|
+
[:add_value, true, nil],
|
215
|
+
[:add_value, false, nil],
|
216
|
+
[:array_end, nil],
|
217
|
+
], handler.calls)
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_file
|
221
|
+
handler = AllSaj.new()
|
222
|
+
p = Oj::Parser.new(:saj)
|
223
|
+
p.handler = handler
|
224
|
+
p.file('saj_test.json')
|
225
|
+
assert_equal([
|
226
|
+
[:array_start, nil],
|
227
|
+
[:add_value, true, nil],
|
228
|
+
[:add_value, false, nil],
|
229
|
+
[:array_end, nil],
|
230
|
+
], handler.calls)
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_default
|
234
|
+
handler = AllSaj.new()
|
235
|
+
json = %|[true]|
|
236
|
+
Oj::Parser.saj.handler = handler
|
237
|
+
Oj::Parser.saj.parse(json)
|
238
|
+
assert_equal([
|
239
|
+
[:array_start, nil],
|
240
|
+
[:add_value, true, nil],
|
241
|
+
[:array_end, nil],
|
242
|
+
], handler.calls)
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|