oj 2.1.3 → 2.1.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +2 -4
- data/ext/oj/compat.c +3 -4
- data/ext/oj/encode.h +51 -0
- data/ext/oj/fast.c +4 -9
- data/ext/oj/object.c +7 -19
- data/ext/oj/oj.c +32 -12
- data/ext/oj/oj.h +3 -0
- data/ext/oj/parse.c +2 -3
- data/ext/oj/saj.c +4 -9
- data/ext/oj/scp.c +5 -12
- data/ext/oj/strict.c +5 -12
- data/lib/oj/version.rb +1 -1
- data/test/a.rb +38 -0
- data/test/bug.rb +15 -0
- data/test/e.rb +12 -0
- data/test/files.rb +29 -0
- data/test/foo.rb +24 -0
- data/test/mj.rb +48 -0
- data/test/perf.rb +107 -0
- data/test/perf_compat.rb +128 -0
- data/test/perf_fast.rb +164 -0
- data/test/perf_object.rb +136 -0
- data/test/perf_saj.rb +109 -0
- data/test/perf_scp.rb +151 -0
- data/test/perf_simple.rb +287 -0
- data/test/perf_strict.rb +127 -0
- data/test/sample.rb +55 -0
- data/test/sample/change.rb +14 -0
- data/test/sample/dir.rb +19 -0
- data/test/sample/doc.rb +36 -0
- data/test/sample/file.rb +48 -0
- data/test/sample/group.rb +16 -0
- data/test/sample/hasprops.rb +16 -0
- data/test/sample/layer.rb +12 -0
- data/test/sample/line.rb +20 -0
- data/test/sample/oval.rb +10 -0
- data/test/sample/rect.rb +10 -0
- data/test/sample/shape.rb +35 -0
- data/test/sample/text.rb +20 -0
- data/test/sample_json.rb +37 -0
- data/test/test_compat.rb +342 -0
- data/test/test_fast.rb +416 -0
- data/test/test_mimic.rb +208 -0
- data/test/test_mimic_after.rb +35 -0
- data/test/test_object.rb +390 -0
- data/test/test_saj.rb +184 -0
- data/test/test_scp.rb +224 -0
- data/test/test_strict.rb +259 -0
- data/test/tests.rb +1017 -0
- data/test/x.rb +59 -0
- metadata +41 -2
data/ext/oj/strict.c
CHANGED
@@ -36,6 +36,7 @@
|
|
36
36
|
#include "oj.h"
|
37
37
|
#include "err.h"
|
38
38
|
#include "parse.h"
|
39
|
+
#include "encode.h"
|
39
40
|
|
40
41
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
41
42
|
#define OJ_INFINITY (1.0/0.0)
|
@@ -53,9 +54,7 @@ static void
|
|
53
54
|
add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
54
55
|
VALUE rstr = rb_str_new(str, len);
|
55
56
|
|
56
|
-
|
57
|
-
rb_enc_associate(rstr, oj_utf8_encoding);
|
58
|
-
#endif
|
57
|
+
rstr = oj_encode(rstr);
|
59
58
|
pi->stack.head->val = rstr;
|
60
59
|
}
|
61
60
|
|
@@ -73,9 +72,7 @@ static VALUE
|
|
73
72
|
hash_key(ParseInfo pi, const char *key, size_t klen) {
|
74
73
|
VALUE rkey = rb_str_new(key, klen);
|
75
74
|
|
76
|
-
|
77
|
-
rb_enc_associate(rkey, oj_utf8_encoding);
|
78
|
-
#endif
|
75
|
+
rkey = oj_encode(rkey);
|
79
76
|
if (Yes == pi->options.sym_key) {
|
80
77
|
rkey = rb_str_intern(rkey);
|
81
78
|
}
|
@@ -86,9 +83,7 @@ static void
|
|
86
83
|
hash_set_cstr(ParseInfo pi, const char *key, size_t klen, const char *str, size_t len, const char *orig) {
|
87
84
|
VALUE rstr = rb_str_new(str, len);
|
88
85
|
|
89
|
-
|
90
|
-
rb_enc_associate(rstr, oj_utf8_encoding);
|
91
|
-
#endif
|
86
|
+
rstr = oj_encode(rstr);
|
92
87
|
rb_hash_aset(stack_peek(&pi->stack)->val, hash_key(pi, key, klen), rstr);
|
93
88
|
}
|
94
89
|
|
@@ -111,9 +106,7 @@ static void
|
|
111
106
|
array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
112
107
|
VALUE rstr = rb_str_new(str, len);
|
113
108
|
|
114
|
-
|
115
|
-
rb_enc_associate(rstr, oj_utf8_encoding);
|
116
|
-
#endif
|
109
|
+
rstr = oj_encode(rstr);
|
117
110
|
rb_ary_push(stack_peek(&pi->stack)->val, rstr);
|
118
111
|
}
|
119
112
|
|
data/lib/oj/version.rb
CHANGED
data/test/a.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW1
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$: << File.dirname(__FILE__)
|
5
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
6
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
7
|
+
|
8
|
+
require 'pp'
|
9
|
+
require 'oj'
|
10
|
+
require 'perf'
|
11
|
+
|
12
|
+
obj = [[1],[2],[3],[4],[5],[6],[7],[8],[9]]
|
13
|
+
obj = [[],[],[],[],[],[],[],[],[]]
|
14
|
+
obj = {
|
15
|
+
'a' => 'Alpha', # string
|
16
|
+
'b' => true, # boolean
|
17
|
+
'c' => 12345, # number
|
18
|
+
'd' => [ true, [false, [12345, nil], 3.967, ['something', false], nil]], # mix it up array
|
19
|
+
'e' => { 'one' => 1, 'two' => 2 }, # hash
|
20
|
+
'f' => nil, # nil
|
21
|
+
'g' => 12345678901234567890123456789, # big number
|
22
|
+
'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
23
|
+
'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
|
24
|
+
}
|
25
|
+
|
26
|
+
json = Oj.dump(obj, mode: :compat)
|
27
|
+
|
28
|
+
puts json
|
29
|
+
#pp Oj.saj_parse(nil, json)
|
30
|
+
pp Oj.t_parse(json)
|
31
|
+
|
32
|
+
if true
|
33
|
+
perf = Perf.new()
|
34
|
+
perf.add('SAJ', 'oj') { Oj.saj_parse(nil, json) }
|
35
|
+
perf.add('T', 'oj') { Oj.t_parse(json) }
|
36
|
+
perf.add('load', 'oj') { Oj.load(json) }
|
37
|
+
perf.run(10000)
|
38
|
+
end
|
data/test/bug.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
|
5
|
+
# required. That can be set in the RUBYOPT environment variable.
|
6
|
+
# export RUBYOPT=-w
|
7
|
+
|
8
|
+
$VERBOSE = true
|
9
|
+
|
10
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
11
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
12
|
+
|
13
|
+
require 'oj'
|
14
|
+
|
15
|
+
Oj.load_file('josh2.json', :mode => :strict)
|
data/test/e.rb
ADDED
data/test/files.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW2
|
2
|
+
|
3
|
+
if $0 == __FILE__
|
4
|
+
$: << '.'
|
5
|
+
$: << '..'
|
6
|
+
$: << '../lib'
|
7
|
+
$: << '../ext'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'pp'
|
11
|
+
require 'sample/file'
|
12
|
+
require 'sample/dir'
|
13
|
+
|
14
|
+
def files(dir)
|
15
|
+
d = ::Sample::Dir.new(dir)
|
16
|
+
Dir.new(dir).each do |fn|
|
17
|
+
next if fn.start_with?('.')
|
18
|
+
filename = File.join(dir, fn)
|
19
|
+
#filename = '.' == dir ? fn : File.join(dir, fn)
|
20
|
+
if File.directory?(filename)
|
21
|
+
d << files(filename)
|
22
|
+
else
|
23
|
+
d << ::Sample::File.new(filename)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
#pp d
|
27
|
+
d
|
28
|
+
end
|
29
|
+
|
data/test/foo.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
|
5
|
+
# required. That can be set in the RUBYOPT environment variable.
|
6
|
+
# export RUBYOPT=-w
|
7
|
+
|
8
|
+
$VERBOSE = true
|
9
|
+
|
10
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
11
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
12
|
+
|
13
|
+
require 'oj'
|
14
|
+
|
15
|
+
reltypes={}
|
16
|
+
Oj::Doc.open_file('foo.json') do |doc|
|
17
|
+
doc.each_child do |target|
|
18
|
+
puts "#{target.local_key} is #{target.local_key.class}"
|
19
|
+
target.each_leaf do |score|
|
20
|
+
reltype=score.local_key
|
21
|
+
reltypes[reltype] = (reltypes[reltype] || 0) + 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/test/mj.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
5
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
6
|
+
# $: << File.join(File.dirname(__FILE__), "../../multi_json/lib")
|
7
|
+
|
8
|
+
require 'multi_json'
|
9
|
+
require 'benchmark'
|
10
|
+
require 'yajl'
|
11
|
+
require 'json'
|
12
|
+
require 'oj'
|
13
|
+
|
14
|
+
iter = 1_000_000
|
15
|
+
iter = 100_000
|
16
|
+
|
17
|
+
json = %({"k1":"val1","k2":"val2","k3":"val3"})
|
18
|
+
obj = { k1: "val1", k2: "val2", k3: "val3" }
|
19
|
+
|
20
|
+
puts "Benchmarks for different JSON handlers with MultiJson."
|
21
|
+
puts " Ruby #{RUBY_VERSION}"
|
22
|
+
puts " #{iter} iterations"
|
23
|
+
|
24
|
+
MultiJson.engine = :oj
|
25
|
+
dt = Benchmark.realtime { iter.times { MultiJson.decode(json) }}
|
26
|
+
et = Benchmark.realtime { iter.times { MultiJson.encode(obj) }}
|
27
|
+
puts " Oj decode: #{dt} encode: #{et}"
|
28
|
+
|
29
|
+
MultiJson.engine = :yajl
|
30
|
+
dt = Benchmark.realtime { iter.times { MultiJson.decode(json) }}
|
31
|
+
et = Benchmark.realtime { iter.times { MultiJson.encode(obj) }}
|
32
|
+
puts " Yajl decode: #{dt} encode: #{et}"
|
33
|
+
|
34
|
+
MultiJson.engine = :json_gem
|
35
|
+
dt = Benchmark.realtime { iter.times { MultiJson.decode(json) }}
|
36
|
+
et = Benchmark.realtime { iter.times { MultiJson.encode(obj) }}
|
37
|
+
puts " Json decode: #{dt} encode: #{et}"
|
38
|
+
|
39
|
+
Oj.default_options = { :mode => :compat, :time_format => :ruby }
|
40
|
+
dt = Benchmark.realtime { iter.times { Oj.load(json) }}
|
41
|
+
et = Benchmark.realtime { iter.times { Oj.dump(obj) }}
|
42
|
+
puts "Raw Oj decode: #{dt} encode: #{et}"
|
43
|
+
|
44
|
+
ye = Yajl::Encoder.new
|
45
|
+
dt = Benchmark.realtime { iter.times { Yajl::Parser.parse(json) }}
|
46
|
+
et = Benchmark.realtime { iter.times { Yajl::Encoder.encode(obj) }}
|
47
|
+
e2 = Benchmark.realtime { iter.times { ye.encode(obj) }}
|
48
|
+
puts "Raw Yajl decode: #{dt} encode: #{et}, encoder: #{e2}"
|
data/test/perf.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
|
2
|
+
class Perf
|
3
|
+
|
4
|
+
def initialize()
|
5
|
+
@items = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add(title, op, &blk)
|
9
|
+
@items << Item.new(title, op, &blk)
|
10
|
+
end
|
11
|
+
|
12
|
+
def before(title, &blk)
|
13
|
+
@items.each do |i|
|
14
|
+
if title == i.title
|
15
|
+
i.set_before(&blk)
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(iter)
|
22
|
+
base = Item.new(nil, nil) { }
|
23
|
+
base.run(iter, 0.0)
|
24
|
+
@items.each do |i|
|
25
|
+
i.run(iter, base.duration)
|
26
|
+
if i.error.nil?
|
27
|
+
puts "#{i.title}.#{i.op} #{iter} times in %0.3f seconds or %0.3f #{i.op}/sec." % [i.duration, iter / i.duration]
|
28
|
+
else
|
29
|
+
puts "***** #{i.title}.#{i.op} failed! #{i.error}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
summary()
|
33
|
+
end
|
34
|
+
|
35
|
+
def summary()
|
36
|
+
fastest = nil
|
37
|
+
slowest = nil
|
38
|
+
width = 6
|
39
|
+
@items.each do |i|
|
40
|
+
next if i.duration.nil?
|
41
|
+
width = i.title.size if width < i.title.size
|
42
|
+
end
|
43
|
+
iva = @items.clone
|
44
|
+
iva.delete_if { |i| i.duration.nil? }
|
45
|
+
iva = iva.sort_by { |i| i.duration }
|
46
|
+
puts
|
47
|
+
puts "Summary:"
|
48
|
+
puts "%*s time (secs) rate (ops/sec)" % [width, 'System']
|
49
|
+
puts "#{'-' * width} ----------- --------------"
|
50
|
+
iva.each do |i|
|
51
|
+
if i.duration.nil?
|
52
|
+
else
|
53
|
+
puts "%*s %11.3f %14.3f" % [width, i.title, i.duration, i.rate ]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
puts
|
57
|
+
puts "Comparison Matrix\n(performance factor, 2.0 means row is twice as fast as column)"
|
58
|
+
puts ([' ' * width] + iva.map { |i| "%*s" % [width, i.title] }).join(' ')
|
59
|
+
puts (['-' * width] + iva.map { |i| '-' * width }).join(' ')
|
60
|
+
iva.each do |i|
|
61
|
+
line = ["%*s" % [width, i.title]]
|
62
|
+
iva.each do |o|
|
63
|
+
line << "%*.2f" % [width, o.duration / i.duration]
|
64
|
+
end
|
65
|
+
puts line.join(' ')
|
66
|
+
end
|
67
|
+
puts
|
68
|
+
end
|
69
|
+
|
70
|
+
class Item
|
71
|
+
attr_accessor :title
|
72
|
+
attr_accessor :op
|
73
|
+
attr_accessor :blk
|
74
|
+
attr_accessor :duration
|
75
|
+
attr_accessor :rate
|
76
|
+
attr_accessor :error
|
77
|
+
|
78
|
+
def initialize(title, op, &blk)
|
79
|
+
@title = title
|
80
|
+
@blk = blk
|
81
|
+
@op = op
|
82
|
+
@duration = nil
|
83
|
+
@rate = nil
|
84
|
+
@error = nil
|
85
|
+
@before = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_before(&blk)
|
89
|
+
@before = blk
|
90
|
+
end
|
91
|
+
|
92
|
+
def run(iter, base)
|
93
|
+
begin
|
94
|
+
GC.start
|
95
|
+
@before.call unless @before.nil?
|
96
|
+
start = Time.now
|
97
|
+
iter.times { @blk.call }
|
98
|
+
@duration = Time.now - start - base
|
99
|
+
@duration = 0.0 if @duration < 0.0
|
100
|
+
@rate = iter / @duration
|
101
|
+
rescue Exception => e
|
102
|
+
@error = "#{e.class}: #{e.message}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end # Item
|
107
|
+
end # Perf
|
data/test/perf_compat.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW1
|
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
|
+
|
12
|
+
$verbose = false
|
13
|
+
$indent = 0
|
14
|
+
$iter = 20000
|
15
|
+
$with_bignum = false
|
16
|
+
$with_nums = true
|
17
|
+
$size = 0
|
18
|
+
|
19
|
+
opts = OptionParser.new
|
20
|
+
opts.on("-v", "verbose") { $verbose = true }
|
21
|
+
opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
|
22
|
+
opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
|
23
|
+
opts.on("-s", "--size [Int]", Integer, "size (~Kbytes)") { |i| $size = i }
|
24
|
+
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
25
|
+
files = opts.parse(ARGV)
|
26
|
+
|
27
|
+
module One
|
28
|
+
module Two
|
29
|
+
module Three
|
30
|
+
class Empty
|
31
|
+
|
32
|
+
def initialize()
|
33
|
+
end
|
34
|
+
|
35
|
+
def eql?(o)
|
36
|
+
self.class == o.class
|
37
|
+
end
|
38
|
+
alias == eql?
|
39
|
+
|
40
|
+
def to_hash()
|
41
|
+
{'json_class' => "#{self.class.name}"}
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_json(*a)
|
45
|
+
%{{"json_class":"#{self.class.name}"}}
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.json_create(h)
|
49
|
+
self.new()
|
50
|
+
end
|
51
|
+
end # Empty
|
52
|
+
end # Three
|
53
|
+
end # Two
|
54
|
+
end # One
|
55
|
+
|
56
|
+
$obj = {
|
57
|
+
'a' => 'Alpha', # string
|
58
|
+
'b' => true, # boolean
|
59
|
+
'c' => 12345, # number
|
60
|
+
'd' => [ true, [false, [-123456789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
|
61
|
+
'e' => { 'zero' => nil, 'one' => 1, 'two' => 2, 'three' => [3], 'four' => [0, 1, 2, 3, 4] }, # hash
|
62
|
+
'f' => nil, # nil
|
63
|
+
'g' => One::Two::Three::Empty.new(),
|
64
|
+
'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
65
|
+
'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
|
66
|
+
}
|
67
|
+
|
68
|
+
Oj.default_options = { :indent => $indent, :mode => :compat }
|
69
|
+
|
70
|
+
if 0 < $size
|
71
|
+
s = Oj.dump($obj, :mode => :compat).size + 1
|
72
|
+
cnt = $size * 1024 / s
|
73
|
+
o = $obj
|
74
|
+
$obj = []
|
75
|
+
cnt.times do
|
76
|
+
$obj << o
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
$json = Oj.dump($obj, :mode => :compat)
|
81
|
+
$failed = {} # key is same as String used in tests later
|
82
|
+
|
83
|
+
def capture_error(tag, orig, load_key, dump_key, &blk)
|
84
|
+
begin
|
85
|
+
obj = blk.call(orig)
|
86
|
+
raise "#{tag} #{dump_key} and #{load_key} did not return the same object as the original." unless orig == obj
|
87
|
+
rescue Exception => e
|
88
|
+
$failed[tag] = "#{e.class}: #{e.message}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Verify that all packages dump and load correctly and return the same Object as the original.
|
93
|
+
capture_error('Oj:compat', $obj, 'load', 'dump') { |o| Oj.compat_load(Oj.dump(o, :mode => :compat)) }
|
94
|
+
capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o|
|
95
|
+
require 'json'
|
96
|
+
require 'json/ext'
|
97
|
+
JSON.generator = JSON::Ext::Generator
|
98
|
+
JSON.parser = JSON::Ext::Parser
|
99
|
+
JSON.load(JSON.generate(o))
|
100
|
+
}
|
101
|
+
|
102
|
+
if $verbose
|
103
|
+
puts "size: #{$json.size}"
|
104
|
+
puts "json:\n#{$json}\n"
|
105
|
+
puts "Oj:compat loaded object:\n#{Oj.compat_load($json)}\n"
|
106
|
+
puts "JSON loaded object:\n#{JSON::Ext::Parser.new($json).parse}\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
puts '-' * 80
|
110
|
+
puts "Compat Parse Performance"
|
111
|
+
perf = Perf.new()
|
112
|
+
unless $failed.has_key?('JSON::Ext')
|
113
|
+
perf.add('JSON::Ext', 'parse') { JSON.load($json) }
|
114
|
+
perf.before('JSON::Ext') { JSON.parser = JSON::Ext::Parser }
|
115
|
+
end
|
116
|
+
unless $failed.has_key?('Oj:compat')
|
117
|
+
perf.add('Oj:compat', 'compat_load') { Oj.compat_load($json) }
|
118
|
+
end
|
119
|
+
perf.run($iter)
|
120
|
+
|
121
|
+
puts
|
122
|
+
puts '-' * 80
|
123
|
+
puts
|
124
|
+
|
125
|
+
unless $failed.empty?
|
126
|
+
puts "The following packages were not included for the reason listed"
|
127
|
+
$failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
|
128
|
+
end
|