oj 3.12.2 → 3.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -3
  3. data/ext/oj/buf.h +9 -0
  4. data/ext/oj/cache.c +193 -0
  5. data/ext/oj/cache.h +20 -0
  6. data/ext/oj/compat.c +8 -22
  7. data/ext/oj/custom.c +15 -14
  8. data/ext/oj/debug.c +132 -0
  9. data/ext/oj/dump.c +12 -15
  10. data/ext/oj/dump_compat.c +3 -3
  11. data/ext/oj/dump_object.c +9 -9
  12. data/ext/oj/dump_strict.c +3 -3
  13. data/ext/oj/err.h +19 -0
  14. data/ext/oj/extconf.rb +4 -0
  15. data/ext/oj/fast.c +7 -18
  16. data/ext/oj/intern.c +398 -0
  17. data/ext/oj/intern.h +27 -0
  18. data/ext/oj/mimic_json.c +9 -9
  19. data/ext/oj/object.c +11 -59
  20. data/ext/oj/odd.c +1 -1
  21. data/ext/oj/oj.c +167 -109
  22. data/ext/oj/oj.h +2 -2
  23. data/ext/oj/parse.c +5 -5
  24. data/ext/oj/parser.c +1512 -0
  25. data/ext/oj/parser.h +90 -0
  26. data/ext/oj/rails.c +5 -5
  27. data/ext/oj/resolve.c +2 -20
  28. data/ext/oj/rxclass.c +1 -1
  29. data/ext/oj/saj.c +1 -1
  30. data/ext/oj/saj2.c +348 -0
  31. data/ext/oj/scp.c +1 -1
  32. data/ext/oj/sparse.c +2 -2
  33. data/ext/oj/stream_writer.c +4 -4
  34. data/ext/oj/strict.c +10 -27
  35. data/ext/oj/string_writer.c +2 -2
  36. data/ext/oj/usual.c +1228 -0
  37. data/ext/oj/validate.c +51 -0
  38. data/ext/oj/wab.c +9 -17
  39. data/lib/oj/error.rb +1 -1
  40. data/lib/oj/mimic.rb +1 -1
  41. data/lib/oj/version.rb +1 -1
  42. data/pages/Modes.md +2 -0
  43. data/pages/Options.md +17 -5
  44. data/pages/Parser.md +309 -0
  45. data/pages/Rails.md +2 -2
  46. data/test/json_gem/json_generator_test.rb +1 -1
  47. data/test/perf_parser.rb +184 -0
  48. data/test/test_hash.rb +1 -1
  49. data/test/test_parser.rb +27 -0
  50. data/test/test_parser_saj.rb +245 -0
  51. data/test/test_parser_usual.rb +213 -0
  52. metadata +22 -5
  53. data/ext/oj/hash.c +0 -168
  54. data/ext/oj/hash.h +0 -21
  55. data/ext/oj/hash_test.c +0 -491
data/pages/Rails.md CHANGED
@@ -41,7 +41,7 @@ The globals that ActiveSupport uses for encoding are:
41
41
 
42
42
  Those globals are aliased to also be accessed from the ActiveSupport module
43
43
  directly so `ActiveSupport::JSON::Encoding.time_precision` can also be accessed
44
- from `ActiveSupport.time_precision`. Oj makes use of these globals in mimicing
44
+ from `ActiveSupport.time_precision`. Oj makes use of these globals in mimicking
45
45
  Rails after the `Oj::Rails.set_encode()` method is called. That also sets the
46
46
  `ActiveSupport.json_encoder` to the `Oj::Rails::Encoder` class.
47
47
 
@@ -125,7 +125,7 @@ gem 'oj', '3.7.12'
125
125
  Ruby which is used by the json gem and by Rails. Ruby varies the
126
126
  significant digits which can be either 16 or 17 depending on the value.
127
127
 
128
- 2. Optimized Hashs do not collapse keys that become the same in the output. As
128
+ 2. Optimized Hashes do not collapse keys that become the same in the output. As
129
129
  an example, a non-String object that has a `to_s()` method will become the
130
130
  return value of the `to_s()` method in the output without checking to see if
131
131
  that has already been used. This could occur is a mix of String and Symbols
@@ -113,7 +113,7 @@ EOT
113
113
  end
114
114
 
115
115
  # TBD Implement JSON.state to return state class.
116
- # set state attibutes from defaults
116
+ # set state attributes from defaults
117
117
  # implement methods
118
118
  # circular should use circular in defaults or maybe always set to true, allow changes with [:check_circular]=
119
119
  def test_states
@@ -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_hash.rb CHANGED
@@ -5,7 +5,7 @@ $: << File.dirname(__FILE__)
5
5
 
6
6
  require 'helper'
7
7
 
8
- class Hashi < Minitest::Test
8
+ class HashTest < Minitest::Test
9
9
 
10
10
  module TestModule
11
11
  end
@@ -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