oj 3.7.4 → 3.13.21
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/CHANGELOG.md +1352 -0
- data/README.md +29 -8
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +53 -72
- data/ext/oj/cache.c +326 -0
- data/ext/oj/cache.h +21 -0
- data/ext/oj/cache8.c +61 -64
- data/ext/oj/cache8.h +12 -39
- data/ext/oj/circarray.c +37 -43
- data/ext/oj/circarray.h +16 -17
- data/ext/oj/code.c +165 -179
- data/ext/oj/code.h +27 -29
- data/ext/oj/compat.c +174 -194
- data/ext/oj/custom.c +809 -866
- data/ext/oj/debug.c +132 -0
- data/ext/oj/dump.c +848 -863
- data/ext/oj/dump.h +81 -67
- data/ext/oj/dump_compat.c +85 -123
- data/ext/oj/dump_leaf.c +100 -188
- data/ext/oj/dump_object.c +527 -656
- data/ext/oj/dump_strict.c +315 -338
- data/ext/oj/encode.h +7 -34
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +40 -29
- data/ext/oj/err.h +48 -48
- data/ext/oj/extconf.rb +17 -4
- data/ext/oj/fast.c +1070 -1087
- data/ext/oj/intern.c +301 -0
- data/ext/oj/intern.h +26 -0
- data/ext/oj/mimic_json.c +469 -436
- data/ext/oj/object.c +525 -593
- data/ext/oj/odd.c +154 -138
- data/ext/oj/odd.h +37 -38
- data/ext/oj/oj.c +1325 -986
- data/ext/oj/oj.h +333 -316
- data/ext/oj/parse.c +1002 -846
- data/ext/oj/parse.h +92 -87
- data/ext/oj/parser.c +1557 -0
- data/ext/oj/parser.h +91 -0
- data/ext/oj/rails.c +888 -878
- data/ext/oj/rails.h +11 -14
- data/ext/oj/reader.c +141 -147
- data/ext/oj/reader.h +73 -89
- data/ext/oj/resolve.c +41 -62
- data/ext/oj/resolve.h +7 -9
- data/ext/oj/rxclass.c +71 -75
- data/ext/oj/rxclass.h +18 -19
- data/ext/oj/saj.c +443 -486
- data/ext/oj/saj2.c +602 -0
- data/ext/oj/scp.c +88 -113
- data/ext/oj/sparse.c +787 -709
- data/ext/oj/stream_writer.c +133 -159
- data/ext/oj/strict.c +127 -118
- data/ext/oj/string_writer.c +230 -249
- data/ext/oj/trace.c +34 -41
- data/ext/oj/trace.h +19 -19
- data/ext/oj/usual.c +1254 -0
- data/ext/oj/util.c +136 -0
- data/ext/oj/util.h +20 -0
- data/ext/oj/val_stack.c +59 -67
- data/ext/oj/val_stack.h +91 -129
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +342 -353
- data/lib/oj/bag.rb +1 -0
- data/lib/oj/easy_hash.rb +5 -4
- data/lib/oj/error.rb +1 -1
- data/lib/oj/json.rb +1 -1
- data/lib/oj/mimic.rb +48 -14
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/state.rb +8 -7
- data/lib/oj/version.rb +2 -2
- data/lib/oj.rb +0 -8
- data/pages/Compatibility.md +1 -1
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +53 -46
- data/pages/Options.md +72 -11
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +73 -22
- data/pages/Security.md +1 -1
- data/test/activerecord/result_test.rb +7 -2
- data/test/activesupport5/abstract_unit.rb +45 -0
- data/test/activesupport5/decoding_test.rb +68 -60
- data/test/activesupport5/encoding_test.rb +111 -96
- data/test/activesupport5/encoding_test_cases.rb +33 -25
- data/test/activesupport5/test_helper.rb +43 -21
- data/test/activesupport5/time_zone_test_helpers.rb +18 -3
- data/test/activesupport6/abstract_unit.rb +44 -0
- data/test/activesupport6/decoding_test.rb +133 -0
- data/test/activesupport6/encoding_test.rb +507 -0
- data/test/activesupport6/encoding_test_cases.rb +98 -0
- data/test/activesupport6/test_common.rb +17 -0
- data/test/activesupport6/test_helper.rb +163 -0
- data/test/activesupport6/time_zone_test_helpers.rb +39 -0
- data/test/activesupport7/abstract_unit.rb +49 -0
- data/test/activesupport7/decoding_test.rb +125 -0
- data/test/activesupport7/encoding_test.rb +486 -0
- data/test/activesupport7/encoding_test_cases.rb +104 -0
- data/test/activesupport7/time_zone_test_helpers.rb +47 -0
- data/test/bar.rb +6 -12
- data/test/baz.rb +16 -0
- data/test/bug.rb +16 -0
- data/test/foo.rb +69 -75
- data/test/helper.rb +16 -0
- data/test/json_gem/json_common_interface_test.rb +8 -3
- data/test/json_gem/json_generator_test.rb +18 -4
- data/test/json_gem/json_parser_test.rb +9 -0
- data/test/json_gem/test_helper.rb +12 -0
- data/test/mem.rb +33 -0
- data/test/perf.rb +1 -1
- data/test/perf_dump.rb +50 -0
- data/test/perf_once.rb +58 -0
- data/test/perf_parser.rb +189 -0
- data/test/perf_scp.rb +11 -10
- data/test/perf_strict.rb +17 -23
- data/test/prec.rb +23 -0
- data/test/sample_json.rb +1 -1
- data/test/test_compat.rb +46 -10
- data/test/test_custom.rb +147 -8
- data/test/test_fast.rb +62 -2
- data/test/test_file.rb +25 -2
- data/test/test_gc.rb +13 -0
- data/test/test_generate.rb +21 -0
- data/test/test_hash.rb +11 -1
- data/test/test_integer_range.rb +7 -2
- data/test/test_object.rb +85 -9
- data/test/test_parser.rb +27 -0
- data/test/test_parser_saj.rb +335 -0
- data/test/test_parser_usual.rb +217 -0
- data/test/test_rails.rb +35 -0
- data/test/test_saj.rb +1 -1
- data/test/test_scp.rb +5 -5
- data/test/test_strict.rb +26 -1
- data/test/test_various.rb +87 -65
- data/test/test_wab.rb +2 -0
- data/test/test_writer.rb +19 -2
- data/test/tests.rb +1 -1
- data/test/zoo.rb +13 -0
- metadata +60 -110
- data/ext/oj/hash.c +0 -163
- data/ext/oj/hash.h +0 -46
- data/ext/oj/hash_test.c +0 -512
data/test/foo.rb
CHANGED
|
@@ -1,83 +1,77 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
# encoding: UTF-8
|
|
3
2
|
|
|
4
|
-
$: <<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
$: << '.'
|
|
4
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
|
5
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
|
6
|
+
|
|
7
|
+
require "oj"
|
|
8
|
+
require "socket"
|
|
9
|
+
require 'io/nonblock'
|
|
10
|
+
|
|
11
|
+
#pid = spawn("nc -d 0.1 -l 5000", out: "/dev/null")
|
|
12
|
+
pid = spawn("nc -i 1 -l 7777", out: "/dev/null")
|
|
13
|
+
at_exit { Process.kill 9, pid }
|
|
14
|
+
sleep 0.2
|
|
15
|
+
s = Socket.tcp("localhost", 7777)
|
|
16
|
+
s.nonblock = false
|
|
17
|
+
1_000_000.times do |x|
|
|
18
|
+
Oj.to_stream(s, { x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]})
|
|
8
19
|
end
|
|
9
20
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@enum = Enumerator.new do |yielder|
|
|
26
|
-
@yielder = yielder
|
|
27
|
-
Oj.sc_parse(self, @test_json)
|
|
21
|
+
=begin
|
|
22
|
+
IO.pipe do |r, w|
|
|
23
|
+
if fork
|
|
24
|
+
r.close
|
|
25
|
+
#w.nonblock = false
|
|
26
|
+
1_000_000.times do |i|
|
|
27
|
+
begin
|
|
28
|
+
Oj.to_stream(w, { x: i})
|
|
29
|
+
rescue IOError => e
|
|
30
|
+
puts "*** #{i} raised #{e.class}: #{e}"
|
|
31
|
+
IO.select(nil, [w])
|
|
32
|
+
retry
|
|
33
|
+
end
|
|
34
|
+
w.puts
|
|
28
35
|
end
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@writer.pop unless @io.eof
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def hash_key(key)
|
|
41
|
-
@writer.push_key(key)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def hash_set(h, key, value)
|
|
45
|
-
@writer.push_value(value)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def array_start
|
|
49
|
-
@writer.push_array
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def array_end
|
|
53
|
-
@writer.pop
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def array_append(a, value)
|
|
57
|
-
yield_data
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def add_value(value);end
|
|
61
|
-
|
|
62
|
-
def yield_data
|
|
63
|
-
@writer.pop_all
|
|
64
|
-
@yielder << @io.string
|
|
65
|
-
@io.reopen("")
|
|
66
|
-
array_start
|
|
36
|
+
else
|
|
37
|
+
w.close
|
|
38
|
+
sleep(0.1)
|
|
39
|
+
r.each_line { |b|
|
|
40
|
+
#print b
|
|
41
|
+
}
|
|
42
|
+
r.close
|
|
43
|
+
Process.exit(0)
|
|
67
44
|
end
|
|
68
45
|
end
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
46
|
+
=end
|
|
47
|
+
|
|
48
|
+
=begin
|
|
49
|
+
IO.pipe do |r, w|
|
|
50
|
+
if fork
|
|
51
|
+
r.close
|
|
52
|
+
#w.nonblock = false
|
|
53
|
+
a = []
|
|
54
|
+
10_000.times do |i|
|
|
55
|
+
a << i
|
|
56
|
+
end
|
|
57
|
+
begin
|
|
58
|
+
Oj.to_stream(w, a, indent: 2)
|
|
59
|
+
rescue IOError => e
|
|
60
|
+
puts "*** raised #{e.class}: #{e}"
|
|
61
|
+
puts "*** fileno: #{w.fileno}"
|
|
62
|
+
puts "*** is an IO?: #{w.kind_of?(IO)}"
|
|
63
|
+
IO.select(nil, [w])
|
|
64
|
+
retry
|
|
65
|
+
end
|
|
66
|
+
w.puts
|
|
67
|
+
else
|
|
68
|
+
w.close
|
|
69
|
+
sleep(0.5)
|
|
70
|
+
r.each_line { |b|
|
|
71
|
+
#print b
|
|
72
|
+
}
|
|
73
|
+
r.close
|
|
74
|
+
Process.exit(0)
|
|
75
|
+
end
|
|
81
76
|
end
|
|
82
|
-
|
|
83
|
-
#{"records":[{"id":1,"name":"record_1"},{"id":2,"name":"record_2"},{"id":3,"name":"record_3"},{"id":4,"name":"record_4"},{"id":5,"name":"record_5"},{"id":6,"name":"record_6"},{"id":7,"name":"record_7"},{"id":8,"name":"record_8"},{"id":9,"name":"record_9"},{"id":10,"name":"record_10"},{"id":11,"name":"record_11"},{"id":12,"name":"record_12"},{"id":13,"name":"record_13"},{"id":14,"name":"record_14"},{"id":15,"name":"record_15"},{"id":16,"name":"record_16"},{"id":17,"name":"record_17"},{"id":18,"name":"record_18"},{"id":19,"name":"record_19"},{"id":20,"name":"record_20"},{"id":21,"name":"record_21"},{"id":22,"name":"record_22"},{"id":23,"name":"record_23"},{"id":24,"name":"record_24"},{"id":25,"name":"record_25"}]}
|
|
77
|
+
=end
|
data/test/helper.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
#
|
|
1
3
|
# Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
|
|
2
4
|
# required. That can be set in the RUBYOPT environment variable.
|
|
3
5
|
# export RUBYOPT=-w
|
|
@@ -16,6 +18,20 @@ require 'bigdecimal'
|
|
|
16
18
|
require 'pp'
|
|
17
19
|
require 'oj'
|
|
18
20
|
|
|
21
|
+
|
|
22
|
+
def verify_gc_compaction
|
|
23
|
+
# This method was added in Ruby 3.0.0. Calling it this way asks the GC to
|
|
24
|
+
# move objects around, helping to find object movement bugs.
|
|
25
|
+
if defined?(GC.verify_compaction_references) == 'method' && !(RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/)
|
|
26
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2.0")
|
|
27
|
+
GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
|
28
|
+
else
|
|
29
|
+
GC.verify_compaction_references(double_heap: true, toward: :empty)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
19
35
|
$ruby = RUBY_DESCRIPTION.split(' ')[0]
|
|
20
36
|
$ruby = 'ree' if 'ruby' == $ruby && RUBY_DESCRIPTION.include?('Ruby Enterprise Edition')
|
|
21
37
|
|
|
@@ -15,7 +15,7 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
|
|
|
15
15
|
def setup
|
|
16
16
|
@hash = {
|
|
17
17
|
'a' => 2,
|
|
18
|
-
'b' =>
|
|
18
|
+
#'b' => 5.23683071,
|
|
19
19
|
'c' => 'c',
|
|
20
20
|
'd' => [ 1, "b", 3.14 ],
|
|
21
21
|
'e' => { 'foo' => 'bar' },
|
|
@@ -23,8 +23,13 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
|
|
|
23
23
|
'h' => 1000.0,
|
|
24
24
|
'i' => 0.001
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
# Tired of chasing floating point rounding and precision. Oj now uses the
|
|
27
|
+
# Ruby float parser in compat mode yet on i386 machines there are issues
|
|
28
|
+
# with this test when the float is included.
|
|
29
|
+
#@json = '{"a":2,"b":5.23683071,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
|
|
30
|
+
#'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
|
|
31
|
+
@json = '{"a":2,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
|
|
32
|
+
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
def test_index
|
|
@@ -72,6 +72,8 @@ EOT
|
|
|
72
72
|
parsed_json = JSON.parse(json)
|
|
73
73
|
assert_equal({"1"=>2}, parsed_json)
|
|
74
74
|
assert_equal '666', JSON.pretty_generate(666)
|
|
75
|
+
json_nil_opts = JSON.pretty_generate({1=>2}, nil)
|
|
76
|
+
assert_equal json, json_nil_opts
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
def test_generate_custom
|
|
@@ -113,7 +115,7 @@ EOT
|
|
|
113
115
|
end
|
|
114
116
|
|
|
115
117
|
# TBD Implement JSON.state to return state class.
|
|
116
|
-
# set state
|
|
118
|
+
# set state attributes from defaults
|
|
117
119
|
# implement methods
|
|
118
120
|
# circular should use circular in defaults or maybe always set to true, allow changes with [:check_circular]=
|
|
119
121
|
def test_states
|
|
@@ -136,6 +138,10 @@ EOT
|
|
|
136
138
|
|
|
137
139
|
def test_pretty_state
|
|
138
140
|
state = JSON::PRETTY_STATE_PROTOTYPE.dup
|
|
141
|
+
# In come cases in Ruby 3.0 an :escape_slash is included in the state. It
|
|
142
|
+
# seems to occur on travis but not locally.
|
|
143
|
+
actual = state.to_h
|
|
144
|
+
actual.delete(:escape_slash)
|
|
139
145
|
assert_equal({
|
|
140
146
|
:allow_nan => false,
|
|
141
147
|
:array_nl => "\n",
|
|
@@ -147,11 +153,15 @@ EOT
|
|
|
147
153
|
:object_nl => "\n",
|
|
148
154
|
:space => " ",
|
|
149
155
|
:space_before => "",
|
|
150
|
-
}.sort_by { |n,| n.to_s },
|
|
156
|
+
}.sort_by { |n,| n.to_s }, actual.sort_by { |n,| n.to_s })
|
|
151
157
|
end
|
|
152
158
|
|
|
153
159
|
def test_safe_state
|
|
154
160
|
state = JSON::SAFE_STATE_PROTOTYPE.dup
|
|
161
|
+
# In come cases in Ruby 3.0 an :escape_slash is included in the state. It
|
|
162
|
+
# seems to occur on travis but not locally.
|
|
163
|
+
actual = state.to_h
|
|
164
|
+
actual.delete(:escape_slash)
|
|
155
165
|
assert_equal({
|
|
156
166
|
:allow_nan => false,
|
|
157
167
|
:array_nl => "",
|
|
@@ -163,11 +173,15 @@ EOT
|
|
|
163
173
|
:object_nl => "",
|
|
164
174
|
:space => "",
|
|
165
175
|
:space_before => "",
|
|
166
|
-
}.sort_by { |n,| n.to_s },
|
|
176
|
+
}.sort_by { |n,| n.to_s }, actual.sort_by { |n,| n.to_s })
|
|
167
177
|
end
|
|
168
178
|
|
|
169
179
|
def test_fast_state
|
|
170
180
|
state = JSON::FAST_STATE_PROTOTYPE.dup
|
|
181
|
+
# In come cases in Ruby 3.0 an :escape_slash is included in the state. It
|
|
182
|
+
# seems to occur on travis but not locally.
|
|
183
|
+
actual = state.to_h
|
|
184
|
+
actual.delete(:escape_slash)
|
|
171
185
|
assert_equal({
|
|
172
186
|
:allow_nan => false,
|
|
173
187
|
:array_nl => "",
|
|
@@ -179,7 +193,7 @@ EOT
|
|
|
179
193
|
:object_nl => "",
|
|
180
194
|
:space => "",
|
|
181
195
|
:space_before => "",
|
|
182
|
-
}.sort_by { |n,| n.to_s },
|
|
196
|
+
}.sort_by { |n,| n.to_s }, actual.sort_by { |n,| n.to_s })
|
|
183
197
|
end
|
|
184
198
|
|
|
185
199
|
def test_allow_nan
|
|
@@ -24,6 +24,8 @@ class JSONParserTest < Test::Unit::TestCase
|
|
|
24
24
|
end if defined?(Encoding::UTF_16)
|
|
25
25
|
|
|
26
26
|
def test_error_message_encoding
|
|
27
|
+
omit 'TruffleRuby causes NameError(<uninitialized constant JSON::Ext>)' if RUBY_ENGINE == 'truffleruby'
|
|
28
|
+
|
|
27
29
|
bug10705 = '[ruby-core:67386] [Bug #10705]'
|
|
28
30
|
json = ".\"\xE2\x88\x9A\"".force_encoding(Encoding::UTF_8)
|
|
29
31
|
e = assert_raise(JSON::ParserError) {
|
|
@@ -269,6 +271,13 @@ EOT
|
|
|
269
271
|
assert_equal too_deep_ary, ok
|
|
270
272
|
ok = JSON.parse too_deep, :max_nesting => 0
|
|
271
273
|
assert_equal too_deep_ary, ok
|
|
274
|
+
|
|
275
|
+
unless ENV['REAL_JSON_GEM']
|
|
276
|
+
# max_nesting should be reset to 0 if not included in options
|
|
277
|
+
# This behavior is not compatible with Ruby standard JSON gem
|
|
278
|
+
ok = JSON.parse too_deep, {}
|
|
279
|
+
assert_equal too_deep_ary, ok
|
|
280
|
+
end
|
|
272
281
|
end
|
|
273
282
|
|
|
274
283
|
def test_backslash
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
$: << File.dirname(__FILE__)
|
|
2
4
|
$oj_dir = File.dirname(File.dirname(File.expand_path(File.dirname(__FILE__))))
|
|
3
5
|
%w(lib ext).each do |dir|
|
|
@@ -12,6 +14,16 @@ if ENV['REAL_JSON_GEM']
|
|
|
12
14
|
else
|
|
13
15
|
require 'oj'
|
|
14
16
|
Oj.mimic_JSON
|
|
17
|
+
|
|
18
|
+
# This method was added in Ruby 3.0.0. Calling it this way asks the GC to
|
|
19
|
+
# move objects around, helping to find object movement bugs.
|
|
20
|
+
if defined?(GC.verify_compaction_references) == 'method'
|
|
21
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2.0")
|
|
22
|
+
GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
|
23
|
+
else
|
|
24
|
+
GC.verify_compaction_references(double_heap: true, toward: :empty)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
15
27
|
end
|
|
16
28
|
|
|
17
29
|
NaN = JSON::NaN if defined?(JSON::NaN)
|
data/test/mem.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
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 'oj'
|
|
9
|
+
|
|
10
|
+
Oj.default_options = { mode: :rails, cache_keys: false, cache_str: -1 }
|
|
11
|
+
|
|
12
|
+
def mem
|
|
13
|
+
`ps -o rss= -p #{$$}`.to_i
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
('a'..'z').each { |a|
|
|
17
|
+
('a'..'z').each { |b|
|
|
18
|
+
('a'..'z').each { |c|
|
|
19
|
+
('a'..'z').each { |d|
|
|
20
|
+
('a'..'z').each { |e|
|
|
21
|
+
('a'..'z').each { |f|
|
|
22
|
+
key = "#{a}#{b}#{c}#{d}#{e}#{f}"
|
|
23
|
+
x = Oj.load(%|{ "#{key}": 101}|)
|
|
24
|
+
#Oj.dump(x)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
puts "#{a}#{b} #{mem}"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Oj::Parser.new(:usual)
|
data/test/perf.rb
CHANGED
data/test/perf_dump.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
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 'oj'
|
|
10
|
+
|
|
11
|
+
$verbose = false
|
|
12
|
+
$indent = 2
|
|
13
|
+
$iter = 100_000
|
|
14
|
+
$size = 2
|
|
15
|
+
|
|
16
|
+
opts = OptionParser.new
|
|
17
|
+
opts.on("-v", "verbose") { $verbose = true }
|
|
18
|
+
opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
|
|
19
|
+
opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
|
|
20
|
+
opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
|
|
21
|
+
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
|
22
|
+
files = opts.parse(ARGV)
|
|
23
|
+
|
|
24
|
+
$obj = {
|
|
25
|
+
'a' => 'Alpha', # string
|
|
26
|
+
'b' => true, # boolean
|
|
27
|
+
'c' => 12345, # number
|
|
28
|
+
'd' => [ true, [false, [-123456789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
|
|
29
|
+
'e' => { 'zero' => nil, 'one' => 1, 'two' => 2, 'three' => [3], 'four' => [0, 1, 2, 3, 4] }, # hash
|
|
30
|
+
'f' => nil, # nil
|
|
31
|
+
'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
|
32
|
+
'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Oj.default_options = { :indent => $indent, :mode => :strict }
|
|
36
|
+
|
|
37
|
+
if 0 < $size
|
|
38
|
+
o = $obj
|
|
39
|
+
$obj = []
|
|
40
|
+
(4 * $size).times do
|
|
41
|
+
$obj << o
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
$json = Oj.dump($obj)
|
|
46
|
+
GC.start
|
|
47
|
+
start = Time.now
|
|
48
|
+
$iter.times { Oj.dump($obj) }
|
|
49
|
+
duration = Time.now - start
|
|
50
|
+
puts "Dumped #{$json.length} byte JSON #{$iter} times in %0.3f seconds or %0.3f iteration/sec." % [duration, $iter / duration]
|
data/test/perf_once.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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 'oj'
|
|
9
|
+
|
|
10
|
+
filename = 'tmp.json'
|
|
11
|
+
File.open(filename, "w") { |f|
|
|
12
|
+
cnt = 0
|
|
13
|
+
f.puts('{')
|
|
14
|
+
('a'..'z').each { |a|
|
|
15
|
+
('a'..'z').each { |b|
|
|
16
|
+
('a'..'z').each { |c|
|
|
17
|
+
('a'..'z').each { |d|
|
|
18
|
+
f.puts(%|"#{a}#{b}#{c}#{d}":#{cnt},|)
|
|
19
|
+
cnt += 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
f.puts('"_last":0}')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
def mem
|
|
28
|
+
`ps -o rss= -p #{$$}`.to_i
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Oj.default_options = { mode: :strict, cache_keys: false, cache_str: -1 }
|
|
32
|
+
start = Time.now
|
|
33
|
+
Oj.load_file('tmp.json')
|
|
34
|
+
dur = Time.now - start
|
|
35
|
+
GC.start
|
|
36
|
+
puts "no cache duration: #{dur} @ #{mem}"
|
|
37
|
+
|
|
38
|
+
Oj.default_options = { cache_keys: true }
|
|
39
|
+
start = Time.now
|
|
40
|
+
Oj.load_file('tmp.json')
|
|
41
|
+
dur = Time.now - start
|
|
42
|
+
GC.start
|
|
43
|
+
puts "initial cache duration: #{dur} @ #{mem}"
|
|
44
|
+
|
|
45
|
+
start = Time.now
|
|
46
|
+
Oj.load_file('tmp.json')
|
|
47
|
+
dur = Time.now - start
|
|
48
|
+
GC.start
|
|
49
|
+
puts "second cache duration: #{dur} @ #{mem}"
|
|
50
|
+
|
|
51
|
+
10.times{ GC.start }
|
|
52
|
+
start = Time.now
|
|
53
|
+
Oj.load_file('tmp.json')
|
|
54
|
+
dur = Time.now - start
|
|
55
|
+
GC.start
|
|
56
|
+
puts "after several GCs cache duration: #{dur} @ #{mem}"
|
|
57
|
+
|
|
58
|
+
# TBD check memory use
|
data/test/perf_parser.rb
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
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: -1, 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
|
+
|
|
168
|
+
p_usual = Oj::Parser.new(:usual)
|
|
169
|
+
p_usual.cache_keys = $cache_keys
|
|
170
|
+
p_usual.cache_strings = ($cache_keys ? 6 : 0)
|
|
171
|
+
p_usual.symbol_keys = $symbol_keys
|
|
172
|
+
p_usual.create_id = '^'
|
|
173
|
+
p_usual.class_cache = true
|
|
174
|
+
p_usual.ignore_json_create = true
|
|
175
|
+
|
|
176
|
+
JSON.create_id = '^'
|
|
177
|
+
|
|
178
|
+
puts '-' * 80
|
|
179
|
+
puts "Parse Usual Object Performance"
|
|
180
|
+
perf = Perf.new()
|
|
181
|
+
perf.add('Oj::Parser.usual', '') { p_usual.parse($obj_json) }
|
|
182
|
+
perf.add('Oj::compat_load', '') { Oj.compat_load($obj_json) }
|
|
183
|
+
perf.add('JSON::Ext', 'parse') { JSON.load($obj_json) }
|
|
184
|
+
perf.run($iter)
|
|
185
|
+
|
|
186
|
+
unless $failed.empty?
|
|
187
|
+
puts "The following packages were not included for the reason listed"
|
|
188
|
+
$failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
|
|
189
|
+
end
|