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.

Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -4
  3. data/ext/oj/compat.c +3 -4
  4. data/ext/oj/encode.h +51 -0
  5. data/ext/oj/fast.c +4 -9
  6. data/ext/oj/object.c +7 -19
  7. data/ext/oj/oj.c +32 -12
  8. data/ext/oj/oj.h +3 -0
  9. data/ext/oj/parse.c +2 -3
  10. data/ext/oj/saj.c +4 -9
  11. data/ext/oj/scp.c +5 -12
  12. data/ext/oj/strict.c +5 -12
  13. data/lib/oj/version.rb +1 -1
  14. data/test/a.rb +38 -0
  15. data/test/bug.rb +15 -0
  16. data/test/e.rb +12 -0
  17. data/test/files.rb +29 -0
  18. data/test/foo.rb +24 -0
  19. data/test/mj.rb +48 -0
  20. data/test/perf.rb +107 -0
  21. data/test/perf_compat.rb +128 -0
  22. data/test/perf_fast.rb +164 -0
  23. data/test/perf_object.rb +136 -0
  24. data/test/perf_saj.rb +109 -0
  25. data/test/perf_scp.rb +151 -0
  26. data/test/perf_simple.rb +287 -0
  27. data/test/perf_strict.rb +127 -0
  28. data/test/sample.rb +55 -0
  29. data/test/sample/change.rb +14 -0
  30. data/test/sample/dir.rb +19 -0
  31. data/test/sample/doc.rb +36 -0
  32. data/test/sample/file.rb +48 -0
  33. data/test/sample/group.rb +16 -0
  34. data/test/sample/hasprops.rb +16 -0
  35. data/test/sample/layer.rb +12 -0
  36. data/test/sample/line.rb +20 -0
  37. data/test/sample/oval.rb +10 -0
  38. data/test/sample/rect.rb +10 -0
  39. data/test/sample/shape.rb +35 -0
  40. data/test/sample/text.rb +20 -0
  41. data/test/sample_json.rb +37 -0
  42. data/test/test_compat.rb +342 -0
  43. data/test/test_fast.rb +416 -0
  44. data/test/test_mimic.rb +208 -0
  45. data/test/test_mimic_after.rb +35 -0
  46. data/test/test_object.rb +390 -0
  47. data/test/test_saj.rb +184 -0
  48. data/test/test_scp.rb +224 -0
  49. data/test/test_strict.rb +259 -0
  50. data/test/tests.rb +1017 -0
  51. data/test/x.rb +59 -0
  52. metadata +41 -2
@@ -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
- #if HAS_ENCODING_SUPPORT
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
- #if HAS_ENCODING_SUPPORT
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
- #if HAS_ENCODING_SUPPORT
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
- #if HAS_ENCODING_SUPPORT
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
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.1.3'
4
+ VERSION = '2.1.4'
5
5
  end
@@ -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
@@ -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)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.join(File.dirname(__FILE__), "../lib")
4
+ $: << File.join(File.dirname(__FILE__), "../ext")
5
+
6
+ require 'oj'
7
+
8
+ json = %{"\xc2\xa9\xc3\x98"}
9
+ puts "original:\n#{json}"
10
+
11
+ str = Oj.load(json)
12
+ puts "out: #{str}"
@@ -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
+
@@ -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
@@ -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}"
@@ -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
@@ -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