oj 0.6.0 → 0.7.0
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.
- data/README.md +146 -86
- data/ext/oj/cache.c +148 -0
- data/ext/oj/cache.h +44 -0
- data/ext/oj/dump.c +308 -154
- data/ext/oj/load.c +262 -19
- data/ext/oj/oj.c +40 -0
- data/ext/oj/oj.h +10 -0
- data/lib/oj/version.rb +1 -1
- data/test/files.rb +29 -0
- data/test/perf.rb +106 -0
- data/test/perf_obj.rb +124 -0
- data/test/perf_strict.rb +131 -0
- data/test/sample.rb +40 -22
- 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/tests.rb +359 -0
- metadata +23 -3
- data/test/simple.rb +0 -208
data/ext/oj/oj.h
CHANGED
@@ -45,6 +45,8 @@ extern "C" {
|
|
45
45
|
#include "ruby/encoding.h"
|
46
46
|
#endif
|
47
47
|
|
48
|
+
#include "cache.h"
|
49
|
+
|
48
50
|
#ifdef JRUBY
|
49
51
|
#define NO_RSTRUCT 1
|
50
52
|
#endif
|
@@ -90,12 +92,20 @@ extern void _oj_raise_error(const char *msg, const char *xml, const char *curren
|
|
90
92
|
|
91
93
|
extern VALUE Oj;
|
92
94
|
|
95
|
+
extern VALUE oj_time_class;
|
96
|
+
|
97
|
+
extern ID oj_at_id;
|
93
98
|
extern ID oj_instance_variables_id;
|
94
99
|
extern ID oj_to_hash_id;
|
95
100
|
extern ID oj_to_json_id;
|
101
|
+
extern ID oj_to_sym_id;
|
102
|
+
extern ID oj_tv_nsec_id;
|
96
103
|
extern ID oj_tv_sec_id;
|
97
104
|
extern ID oj_tv_usec_id;
|
98
105
|
|
106
|
+
extern Cache oj_class_cache;
|
107
|
+
extern Cache oj_attr_cache;
|
108
|
+
|
99
109
|
#if defined(__cplusplus)
|
100
110
|
#if 0
|
101
111
|
{ /* satisfy cc-mode */
|
data/lib/oj/version.rb
CHANGED
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/perf.rb
ADDED
@@ -0,0 +1,106 @@
|
|
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.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 row is means 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
|
+
@before.call unless @before.nil?
|
95
|
+
start = Time.now
|
96
|
+
iter.times { @blk.call }
|
97
|
+
@duration = Time.now - start - base
|
98
|
+
@duration = 0.0 if @duration < 0.0
|
99
|
+
@rate = iter / @duration
|
100
|
+
rescue Exception => e
|
101
|
+
@error = "#{e.class}: #{e.message}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end # Item
|
106
|
+
end # Perf
|
data/test/perf_obj.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW1
|
2
|
+
|
3
|
+
$: << '.'
|
4
|
+
$: << '../lib'
|
5
|
+
$: << '../ext'
|
6
|
+
|
7
|
+
if __FILE__ == $0
|
8
|
+
if (i = ARGV.index('-I'))
|
9
|
+
x,path = ARGV.slice!(i, 2)
|
10
|
+
$: << path
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'optparse'
|
15
|
+
require 'ox'
|
16
|
+
require 'oj'
|
17
|
+
require 'perf'
|
18
|
+
require 'sample'
|
19
|
+
require 'files'
|
20
|
+
|
21
|
+
$circular = false
|
22
|
+
$indent = 0
|
23
|
+
|
24
|
+
do_sample = false
|
25
|
+
do_files = false
|
26
|
+
|
27
|
+
do_load = false
|
28
|
+
do_dump = false
|
29
|
+
do_read = false
|
30
|
+
do_write = false
|
31
|
+
$iter = 1000
|
32
|
+
|
33
|
+
opts = OptionParser.new
|
34
|
+
opts.on("-c", "circular options") { $circular = true }
|
35
|
+
|
36
|
+
opts.on("-s", "load and dump as sample Ruby object") { do_sample = true }
|
37
|
+
opts.on("-f", "load and dump as files Ruby object") { do_files = true }
|
38
|
+
|
39
|
+
opts.on("-l", "load") { do_load = true }
|
40
|
+
opts.on("-d", "dump") { do_dump = true }
|
41
|
+
opts.on("-r", "read") { do_read = true }
|
42
|
+
opts.on("-w", "write") { do_write = true }
|
43
|
+
opts.on("-a", "load, dump, read and write") { do_load = true; do_dump = true; do_read = true; do_write = true }
|
44
|
+
|
45
|
+
opts.on("-i", "--iterations [Int]", Integer, "iterations") { |i| $iter = i }
|
46
|
+
|
47
|
+
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
48
|
+
files = opts.parse(ARGV)
|
49
|
+
|
50
|
+
$obj = nil
|
51
|
+
$xml = nil
|
52
|
+
$mars = nil
|
53
|
+
$json = nil
|
54
|
+
|
55
|
+
unless do_load || do_dump || do_read || do_write
|
56
|
+
do_load = true
|
57
|
+
do_dump = true
|
58
|
+
do_read = true
|
59
|
+
do_write = true
|
60
|
+
end
|
61
|
+
|
62
|
+
# prepare all the formats for input
|
63
|
+
if files.empty?
|
64
|
+
$obj = do_sample ? sample_doc(2) : files('..')
|
65
|
+
$mars = Marshal.dump($obj)
|
66
|
+
$xml = Ox.dump($obj, :indent => $indent, circular: $circular)
|
67
|
+
$json = Oj.dump($obj, :indent => $indent, circular: $circular)
|
68
|
+
File.open('sample.xml', 'w') { |f| f.write($xml) }
|
69
|
+
File.open('sample.json', 'w') { |f| f.write($json) }
|
70
|
+
File.open('sample.marshal', 'w') { |f| f.write($mars) }
|
71
|
+
else
|
72
|
+
puts "loading and parsing #{files}\n\n"
|
73
|
+
# TBD change to allow xml and json
|
74
|
+
data = files.map do |f|
|
75
|
+
$xml = File.read(f)
|
76
|
+
$obj = Ox.load($xml);
|
77
|
+
$mars = Marshal.dump($obj)
|
78
|
+
$json = Oj.dump($obj, :indent => $indent, circular: $circular)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Oj.default_options = { :mode => :object, :indent => $indent }
|
83
|
+
|
84
|
+
if do_load
|
85
|
+
puts '-' * 80
|
86
|
+
puts "Load Performance"
|
87
|
+
perf = Perf.new()
|
88
|
+
perf.add('Ox', 'load') { Ox.load($xml, :mode => :object) }
|
89
|
+
perf.add('Oj', 'load') { Oj.load($json) }
|
90
|
+
perf.add('Marshal', 'load') { Marshal.load($mars) }
|
91
|
+
perf.run($iter)
|
92
|
+
end
|
93
|
+
|
94
|
+
if do_dump
|
95
|
+
puts '-' * 80
|
96
|
+
puts "Dump Performance"
|
97
|
+
perf = Perf.new()
|
98
|
+
perf.add('Ox', 'dump') { Ox.dump($obj, :indent => $indent, :circular => $circular) }
|
99
|
+
perf.add('Oj', 'dump') { Oj.dump($obj) }
|
100
|
+
perf.add('Marshal', 'dump') { Marshal.dump($obj) }
|
101
|
+
perf.run($iter)
|
102
|
+
end
|
103
|
+
|
104
|
+
if do_read
|
105
|
+
puts '-' * 80
|
106
|
+
puts "Read from file Performance"
|
107
|
+
perf = Perf.new()
|
108
|
+
perf.add('Ox', 'load_file') { Ox.load_file('sample.xml', :mode => :object) }
|
109
|
+
perf.add('Oj', 'load') { Oj.load_file('sample.json') }
|
110
|
+
perf.add('Marshal', 'load') { Marshal.load(File.new('sample.marshal')) }
|
111
|
+
perf.run($iter)
|
112
|
+
end
|
113
|
+
|
114
|
+
if do_write
|
115
|
+
puts '-' * 80
|
116
|
+
puts "Write to file Performance"
|
117
|
+
perf = Perf.new()
|
118
|
+
perf.add('Ox', 'to_file') { Ox.to_file('sample.xml', $obj, :indent => $indent, :circular => $circular) }
|
119
|
+
perf.add('Oj', 'to_file') { Oj.to_file('sample.json', $obj) }
|
120
|
+
perf.add('Marshal', 'dump') { Marshal.dump($obj, File.new('sample.marshal', 'w')) }
|
121
|
+
perf.run($iter)
|
122
|
+
end
|
123
|
+
|
124
|
+
|
data/test/perf_strict.rb
ADDED
@@ -0,0 +1,131 @@
|
|
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 'yajl'
|
10
|
+
require 'perf'
|
11
|
+
require 'json'
|
12
|
+
require 'json/pure'
|
13
|
+
require 'json/ext'
|
14
|
+
require 'msgpack'
|
15
|
+
require 'oj'
|
16
|
+
require 'ox'
|
17
|
+
|
18
|
+
class Jazz
|
19
|
+
def initialize()
|
20
|
+
@boolean = true
|
21
|
+
@number = 58
|
22
|
+
@string = "A string"
|
23
|
+
@array = [true, false, nil]
|
24
|
+
@hash = { 'one' => 1, 'two' => 2 }
|
25
|
+
end
|
26
|
+
def to_json(*) # Yajl and JSON have different signatures
|
27
|
+
%{
|
28
|
+
{ "boolean":#{@boolean},
|
29
|
+
"number":#{@number},
|
30
|
+
"string":#{@string},
|
31
|
+
"array":#{@array},
|
32
|
+
"hash":#{@hash},
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
def to_hash()
|
37
|
+
{ 'boolean' => @boolean,
|
38
|
+
'number' => @number,
|
39
|
+
'string' => @string,
|
40
|
+
'array' => @array,
|
41
|
+
'hash' => @hash,
|
42
|
+
}
|
43
|
+
end
|
44
|
+
def to_msgpack(out)
|
45
|
+
out << MessagePack.pack(to_hash())
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
$indent = 0
|
50
|
+
$iter = 100000
|
51
|
+
$with_object = true
|
52
|
+
$with_bignum = true
|
53
|
+
$with_nums = true
|
54
|
+
|
55
|
+
opts = OptionParser.new
|
56
|
+
opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
|
57
|
+
opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
|
58
|
+
opts.on("-o", "without objects") { $with_object = false }
|
59
|
+
opts.on("-b", "without bignum") { $with_bignum = false }
|
60
|
+
opts.on("-n", "without numbers") { $with_nums = false }
|
61
|
+
opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
|
62
|
+
files = opts.parse(ARGV)
|
63
|
+
|
64
|
+
if $with_nums
|
65
|
+
$obj = {
|
66
|
+
'a' => 'Alpha',
|
67
|
+
'b' => true,
|
68
|
+
'c' => 12345,
|
69
|
+
'd' => [ true, [false, [12345, nil], 3.967, ['something', false], nil]],
|
70
|
+
'e' => { 'one' => 1, 'two' => 2 },
|
71
|
+
'f' => nil,
|
72
|
+
}
|
73
|
+
$obj['g'] = Jazz.new() if $with_object
|
74
|
+
$obj['h'] = 12345678901234567890123456789 if $with_bignum
|
75
|
+
else
|
76
|
+
$obj = {
|
77
|
+
'a' => 'Alpha',
|
78
|
+
'b' => true,
|
79
|
+
'c' => '12345',
|
80
|
+
'd' => [ true, [false, ['12345', nil], '3.967', ['something', false], nil]],
|
81
|
+
'e' => { 'one' => '1', 'two' => '2' },
|
82
|
+
'f' => nil,
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
Oj.default_options = { :indent => $indent, :mode => :compat }
|
87
|
+
Ox.default_options = { :indent => $indent, :mode => :object }
|
88
|
+
|
89
|
+
$json = Oj.dump($obj)
|
90
|
+
$xml = Ox.dump($obj, :indent => $indent)
|
91
|
+
begin
|
92
|
+
$msgpack = MessagePack.pack($obj)
|
93
|
+
rescue Exception => e
|
94
|
+
puts "MessagePack failed to pack! #{e.class}: #{e.message}.\nSkipping."
|
95
|
+
$msgpack = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
puts '-' * 80
|
99
|
+
puts "Load/Parse Performance"
|
100
|
+
perf = Perf.new()
|
101
|
+
perf.add('Oj', 'load') { Oj.load($json) }
|
102
|
+
perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) }
|
103
|
+
perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse }
|
104
|
+
perf.add('JSON::Pure', 'parse') { JSON::Pure::Parser.new($json).parse }
|
105
|
+
perf.add('Ox', 'load') { Ox.load($xml) }
|
106
|
+
perf.add('MessagePack', 'unpack') { MessagePack.unpack($msgpack) } unless $msgpack.nil?
|
107
|
+
perf.run($iter)
|
108
|
+
|
109
|
+
puts
|
110
|
+
puts '-' * 80
|
111
|
+
puts "Dump/Encode/Generate Performance"
|
112
|
+
perf = Perf.new()
|
113
|
+
perf.add('Oj', 'dump') { Oj.dump($obj) }
|
114
|
+
perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) }
|
115
|
+
if 0 == $indent
|
116
|
+
perf.add('JSON::Ext', 'generate') { JSON.generate($obj) }
|
117
|
+
else
|
118
|
+
perf.add('JSON::Ext', 'generate') { JSON.pretty_generate($obj) }
|
119
|
+
end
|
120
|
+
perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
|
121
|
+
if 0 == $indent
|
122
|
+
perf.add('JSON::Pure', 'generate') { JSON.generate($obj) }
|
123
|
+
else
|
124
|
+
perf.add('JSON::Pure', 'generate') { JSON.pretty_generate($obj) }
|
125
|
+
end
|
126
|
+
perf.before('JSON::Pure') { JSON.generator = JSON::Pure::Generator }
|
127
|
+
perf.add('Ox', 'dump') { Ox.dump($obj) }
|
128
|
+
perf.add('MessagePack', 'pack') { MessagePack.pack($obj) } unless $msgpack.nil?
|
129
|
+
perf.run($iter)
|
130
|
+
|
131
|
+
puts
|
data/test/sample.rb
CHANGED
@@ -8,30 +8,48 @@ if $0 == __FILE__
|
|
8
8
|
end
|
9
9
|
|
10
10
|
require 'pp'
|
11
|
-
require '
|
11
|
+
require 'sample/doc'
|
12
12
|
|
13
|
-
|
13
|
+
|
14
|
+
def sample_doc(size=3)
|
14
15
|
colors = [ :black, :gray, :white, :red, :blue, :yellow, :green, :purple, :orange ]
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
'height' => i,
|
22
|
-
'depth' => i,
|
23
|
-
'weight' => i * 1.3,
|
24
|
-
'address' => {
|
25
|
-
'street' => "#{i} Main Street",
|
26
|
-
'city' => 'Sity',
|
27
|
-
'state' => nil
|
28
|
-
}
|
29
|
-
}
|
30
|
-
container << box
|
16
|
+
|
17
|
+
d = ::Sample::Doc.new('Sample')
|
18
|
+
|
19
|
+
# add some history
|
20
|
+
(0..size * 10).each do |i|
|
21
|
+
d.add_change("Changed at t+#{i}.")
|
31
22
|
end
|
32
|
-
container
|
33
|
-
end
|
34
23
|
|
35
|
-
|
36
|
-
|
24
|
+
# add some layers
|
25
|
+
(1..size).each do |i|
|
26
|
+
layer = ::Sample::Layer.new("Layer-#{i}")
|
27
|
+
(1..size).each do |j|
|
28
|
+
g = ::Sample::Group.new
|
29
|
+
(1..size).each do |k|
|
30
|
+
g2 = ::Sample::Group.new
|
31
|
+
r = ::Sample::Rect.new(j * 40 + 10.0, i * 10.0,
|
32
|
+
10.123456 / k, 10.0 / k, colors[(i + j + k) % colors.size])
|
33
|
+
r.add_prop(:part_of, layer.name)
|
34
|
+
g2 << r
|
35
|
+
g2 << ::Sample::Text.new("#{k} in #{j}", r.left, r.top, r.width, r.height)
|
36
|
+
g << g2
|
37
|
+
end
|
38
|
+
g2 = ::Sample::Group.new
|
39
|
+
(1..size).each do |k|
|
40
|
+
o = ::Sample::Oval.new(j * 40 + 12.0, i * 10.0 + 2.0,
|
41
|
+
6.0 / k, 6.0 / k, colors[(i + j + k) % colors.size])
|
42
|
+
o.add_prop(:inside, true)
|
43
|
+
g << o
|
44
|
+
end
|
45
|
+
g << g2
|
46
|
+
layer << g
|
47
|
+
end
|
48
|
+
d.layers[layer.name] = layer
|
49
|
+
end
|
50
|
+
|
51
|
+
# some properties
|
52
|
+
d.add_prop(:purpose, 'an example')
|
53
|
+
|
54
|
+
d
|
37
55
|
end
|