oj 1.0.6 → 1.1.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.

@@ -64,6 +64,12 @@ extern "C" {
64
64
  #define IVAR_HELPERS 0
65
65
  #endif
66
66
 
67
+ #if IVAR_HELPERS
68
+ #include "ruby/st.h"
69
+ #else
70
+ #include "st.h"
71
+ #endif
72
+
67
73
  #define raise_error(msg, xml, current) _oj_raise_error(msg, xml, current, __FILE__, __LINE__)
68
74
 
69
75
  typedef enum {
@@ -79,6 +85,19 @@ typedef enum {
79
85
  CompatMode = 'c'
80
86
  } Mode;
81
87
 
88
+ typedef struct _DumpOpts {
89
+ const char *indent;
90
+ const char *before_sep;
91
+ const char *after_sep;
92
+ const char *hash_nl;
93
+ const char *array_nl;
94
+ uint8_t indent_size;
95
+ uint8_t before_size;
96
+ uint8_t after_size;
97
+ uint8_t hash_size;
98
+ uint8_t array_size;
99
+ } *DumpOpts;
100
+
82
101
  typedef struct _Options {
83
102
  char encoding[64]; // encoding, stored in the option to avoid GC invalidation in default values
84
103
  int indent; // indention for dump, default 2
@@ -87,6 +106,7 @@ typedef struct _Options {
87
106
  char sym_key; // YesNo
88
107
  char ascii_only; // YesNo
89
108
  char mode; // Mode
109
+ DumpOpts dump_opts;
90
110
  } *Options;
91
111
 
92
112
  enum {
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '1.0.6'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -91,6 +91,7 @@ class Perf
91
91
 
92
92
  def run(iter, base)
93
93
  begin
94
+ GC.start
94
95
  @before.call unless @before.nil?
95
96
  start = Time.now
96
97
  iter.times { @blk.call }
@@ -18,6 +18,7 @@ $iter = 10000
18
18
  $gets = 0
19
19
  $fetch = false
20
20
  $write = false
21
+ $read = false
21
22
 
22
23
  opts = OptionParser.new
23
24
  opts.on("-v", "verbose") { $verbose = true }
@@ -26,6 +27,7 @@ opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
26
27
  opts.on("-g", "--gets [Int]", Integer, "number of gets") { |i| $gets = i }
27
28
  opts.on("-f", "fetch") { $fetch = true }
28
29
  opts.on("-w", "write") { $write = true }
30
+ opts.on("-r", "read") { $read = true }
29
31
  opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
30
32
  files = opts.parse(ARGV)
31
33
 
@@ -139,6 +141,23 @@ if $write
139
141
  end
140
142
  end
141
143
 
144
+ if $read
145
+ puts '-' * 80
146
+ puts "JSON read from file Performance"
147
+ Oj::Doc.open($json) { |doc| doc.dump(nil, 'oj.json') }
148
+ File.open('yajl.json', 'w') { |f| Yajl::Encoder.encode($obj, f) }
149
+ JSON.generator = JSON::Ext::Generator
150
+ File.open('json_ext.json', 'w') { |f| f.write(JSON.fast_generate($obj)) }
151
+ Oj::Doc.open($json) do |doc|
152
+ perf = Perf.new()
153
+ perf.add('Oj::Doc', 'open_file') { ::Oj::Doc.open_file('oj.json') }
154
+ perf.add('Yajl', 'decode') { Yajl::decoder.decode(File.read('yajl.json')) }
155
+ perf.add('JSON::Ext', '') { JSON.parse(File.read('json_ext.json')) }
156
+ perf.before('JSON::Ext') { JSON.parser = JSON::Ext::Parser }
157
+ perf.run($iter)
158
+ end
159
+ end
160
+
142
161
  unless $failed.empty?
143
162
  puts "The following packages were not included for the reason listed"
144
163
  $failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
@@ -65,7 +65,7 @@ end
65
65
 
66
66
  $verbose = false
67
67
  $indent = 0
68
- $iter = 100000
68
+ $iter = 10000
69
69
  $with_object = true
70
70
  $with_bignum = true
71
71
  $with_nums = true
@@ -130,8 +130,16 @@ capture_error('Oj', $obj, 'load', 'dump') { |o| Oj.load(Oj.dump(o, :mode => :com
130
130
  capture_error('Ox', $obj, 'load', 'dump') { |o| Ox.load(Ox.dump(o, :mode => :object), :mode => :object) }
131
131
  capture_error('MessagePack', $obj, 'unpack', 'pack') { |o| MessagePack.unpack(MessagePack.pack($obj)) }
132
132
  capture_error('Yajl', $obj, 'encode', 'parse') { |o| Yajl::Parser.parse(Yajl::Encoder.encode(o)) }
133
- capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o| JSON.generator = JSON::Ext::Generator; JSON::Ext::Parser.new(JSON.generate(o)).parse }
134
- capture_error('JSON::Pure', $obj, 'generate', 'parse') { |o| JSON.generator = JSON::Pure::Generator; JSON::Pure::Parser.new(JSON.generate(o)).parse }
133
+ capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o|
134
+ JSON.generator = JSON::Ext::Generator
135
+ JSON.parser = JSON::Ext::Parser
136
+ JSON.parse(JSON.generate(o))
137
+ }
138
+ capture_error('JSON::Pure', $obj, 'generate', 'parse') { |o|
139
+ JSON.generator = JSON::Pure::Generator
140
+ JSON.parser = JSON::Pure::Parser
141
+ JSON.parse(JSON.generate(o))
142
+ }
135
143
 
136
144
  begin
137
145
  $msgpack = MessagePack.pack($obj)
@@ -150,6 +158,14 @@ end
150
158
  puts '-' * 80
151
159
  puts "Load/Parse Performance"
152
160
  perf = Perf.new()
161
+ unless $failed.has_key?('JSON::Ext')
162
+ perf.add('JSON::Ext', 'parse') { JSON.parse($json) }
163
+ perf.before('JSON::Ext') { JSON.parser = JSON::Ext::Parser }
164
+ end
165
+ unless $failed.has_key?('JSON::Pure')
166
+ perf.add('JSON::Pure', 'parse') { JSON.parse($json) }
167
+ perf.before('JSON::Pure') { JSON.parser = JSON::Pure::Parser }
168
+ end
153
169
  unless $failed.has_key?('Oj:compat')
154
170
  perf.add('Oj:compat', 'load') { Oj.load($json) }
155
171
  perf.before('Oj:compat') { Oj.default_options = { :mode => :compat} }
@@ -159,8 +175,6 @@ unless $failed.has_key?('Oj')
159
175
  perf.before('Oj') { Oj.default_options = { :mode => :object} }
160
176
  end
161
177
  perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
162
- perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
163
- perf.add('JSON::Pure', 'parse') { JSON::Pure::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
164
178
  perf.add('Ox', 'load') { Ox.load($xml) } unless $failed.has_key?('Ox')
165
179
  perf.add('MessagePack', 'unpack') { MessagePack.unpack($msgpack) } unless $failed.has_key?('MessagePack')
166
180
  perf.run($iter)
@@ -169,15 +183,6 @@ puts
169
183
  puts '-' * 80
170
184
  puts "Dump/Encode/Generate Performance"
171
185
  perf = Perf.new()
172
- unless $failed.has_key?('Oj:compat')
173
- perf.add('Oj:compat', 'dump') { Oj.dump($obj) }
174
- perf.before('Oj:compat') { Oj.default_options = { :mode => :compat} }
175
- end
176
- unless $failed.has_key?('Oj')
177
- perf.add('Oj', 'dump') { Oj.dump($obj) }
178
- perf.before('Oj') { Oj.default_options = { :mode => :object} }
179
- end
180
- perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) } unless $failed.has_key?('Yajl')
181
186
  unless $failed.has_key?('JSON::Ext')
182
187
  if 0 == $indent
183
188
  perf.add('JSON::Ext', 'generate') { JSON.generate($obj) }
@@ -194,6 +199,15 @@ unless $failed.has_key?('JSON::Pure')
194
199
  end
195
200
  perf.before('JSON::Pure') { JSON.generator = JSON::Pure::Generator }
196
201
  end
202
+ unless $failed.has_key?('Oj')
203
+ perf.add('Oj', 'dump') { Oj.dump($obj) }
204
+ perf.before('Oj') { Oj.default_options = { :mode => :object} }
205
+ end
206
+ unless $failed.has_key?('Oj:compat')
207
+ perf.add('Oj:compat', 'dump') { Oj.dump($obj) }
208
+ perf.before('Oj:compat') { Oj.default_options = { :mode => :compat} }
209
+ end
210
+ perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) } unless $failed.has_key?('Yajl')
197
211
  perf.add('Ox', 'dump') { Ox.dump($obj) } unless $failed.has_key?('Ox')
198
212
  perf.add('MessagePack', 'pack') { MessagePack.pack($obj) } unless $failed.has_key?('MessagePack')
199
213
  perf.run($iter)
@@ -310,6 +310,43 @@ class DocTest < ::Test::Unit::TestCase
310
310
  end
311
311
  end
312
312
 
313
+ def test_open_close
314
+ json = %{{"a":[1,2,3]}}
315
+ doc = Oj::Doc.open(json)
316
+ assert_equal(Oj::Doc, doc.class)
317
+ assert_equal(5, doc.size)
318
+ assert_equal('/', doc.where?)
319
+ doc.move('a/1')
320
+ doc.home()
321
+ assert_equal(2, doc.fetch('/a/2'))
322
+ assert_equal(2, doc.fetch('a/2'))
323
+ doc.close()
324
+ begin
325
+ doc.home()
326
+ rescue Exception => e
327
+ assert(true)
328
+ end
329
+ end
330
+
331
+ def test_file_open_close
332
+ filename = 'open_file_test.json'
333
+ File.open(filename, 'w') { |f| f.write('{"a":[1,2,3]}') }
334
+ doc = Oj::Doc.open_file(filename)
335
+ assert_equal(Oj::Doc, doc.class)
336
+ assert_equal(5, doc.size)
337
+ assert_equal('/', doc.where?)
338
+ doc.move('a/1')
339
+ doc.home()
340
+ assert_equal(2, doc.fetch('/a/2'))
341
+ assert_equal(2, doc.fetch('a/2'))
342
+ doc.close()
343
+ begin
344
+ doc.home()
345
+ rescue Exception => e
346
+ assert(true)
347
+ end
348
+ end
349
+
313
350
  def test_dump
314
351
  Oj::Doc.open('[1,[2,3]]') do |doc|
315
352
  assert_equal('[1,[2,3]]', doc.dump())
@@ -328,4 +365,21 @@ class DocTest < ::Test::Unit::TestCase
328
365
  assert_equal({'/1' => 1, '/2/1' => 2, '/2/2' => 3}, results)
329
366
  end
330
367
 
368
+ def test_comment
369
+ json = %{{
370
+ "x"/*one*/:/*two*/true,//three
371
+ "y":58/*four*/,
372
+ "z": [1,2/*five*/,
373
+ 3 // six
374
+ ]
375
+ }
376
+ }
377
+ results = Oj::Doc.open(json) do |doc|
378
+ h = {}
379
+ doc.each_leaf() { |d| h[d.where?] = d.fetch() }
380
+ h
381
+ end
382
+ assert_equal({'/x' => true, '/y' => 58, '/z/1' => 1, '/z/2' => 2, '/z/3' => 3}, results)
383
+ end
384
+
331
385
  end # DocTest
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby -wW1
2
+ # encoding: UTF-8
3
+
4
+ $: << File.join(File.dirname(__FILE__), "../lib")
5
+ $: << File.join(File.dirname(__FILE__), "../ext")
6
+
7
+ require 'test/unit'
8
+ require 'stringio'
9
+ require 'oj'
10
+
11
+ class Jam
12
+ attr_accessor :x, :y
13
+
14
+ def initialize(x, y)
15
+ @x = x
16
+ @y = y
17
+ end
18
+
19
+ def eql?(o)
20
+ self.class == o.class && @x == o.x && @y == o.y
21
+ end
22
+ alias == eql?
23
+
24
+ def to_json()
25
+ %{{"json_class":"#{self.class}","x":#{@x},"y":#{@y}}}
26
+ end
27
+
28
+ def self.json_create(h)
29
+ self.new(h['x'], h['y'])
30
+ end
31
+
32
+ end # Jam
33
+
34
+ class Mimic < ::Test::Unit::TestCase
35
+
36
+ def test0_mimic_json
37
+ assert(defined?(JSON).nil?)
38
+ Oj.mimic_JSON
39
+ assert(!defined?(JSON).nil?)
40
+ end
41
+
42
+ # dump
43
+ def test_dump_string
44
+ json = JSON.dump([1, true, nil])
45
+ assert_equal(%{[1,true,null]}, json)
46
+ end
47
+
48
+ def test_dump_io
49
+ s = StringIO.new()
50
+ json = JSON.dump([1, true, nil], s)
51
+ assert_equal(s, json)
52
+ assert_equal(%{[1,true,null]}, s.string)
53
+ end
54
+ # TBD options
55
+
56
+ # load
57
+ def test_load_string
58
+ json = %{{"a":1,"b":[true,false]}}
59
+ obj = JSON.load(json)
60
+ assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
61
+ end
62
+
63
+ def test_load_io
64
+ json = %{{"a":1,"b":[true,false]}}
65
+ obj = JSON.load(StringIO.new(json))
66
+ assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
67
+ end
68
+
69
+ def test_load_proc
70
+ children = []
71
+ json = %{{"a":1,"b":[true,false]}}
72
+ p = Proc.new {|x| children << x }
73
+ obj = JSON.load(json, p)
74
+ assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
75
+ assert_equal([1, true, false, [true, false], { 'a' => 1, 'b' => [true, false]}], children)
76
+ end
77
+
78
+ # []
79
+ def test_bracket_load
80
+ json = %{{"a":1,"b":[true,false]}}
81
+ obj = JSON[json]
82
+ assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
83
+ end
84
+
85
+ def test_bracket_dump
86
+ json = JSON[[1, true, nil]]
87
+ assert_equal(%{[1,true,null]}, json)
88
+ end
89
+
90
+ # generate
91
+ def test_generate
92
+ json = JSON.generate({ 'a' => 1, 'b' => [true, false]})
93
+ assert_equal(%{{"a":1,"b":[true,false]}}, json)
94
+ end
95
+ def test_generate_options
96
+ json = JSON.generate({ 'a' => 1, 'b' => [true, false]},
97
+ :indent => "--",
98
+ :array_nl => "\n",
99
+ :object_nl => "#\n",
100
+ :space => "*",
101
+ :space_before => "~")
102
+ assert_equal(%{{#
103
+ --"a"~:*1,#
104
+ --"b"~:*[
105
+ ----true,
106
+ ----false
107
+ --]#
108
+ }}, json)
109
+ end
110
+
111
+ # fast_generate
112
+ def test_fast_generate
113
+ json = JSON.generate({ 'a' => 1, 'b' => [true, false]})
114
+ assert_equal(%{{"a":1,"b":[true,false]}}, json)
115
+ end
116
+
117
+ # pretty_generate
118
+ def test_pretty_generate
119
+ json = JSON.pretty_generate({ 'a' => 1, 'b' => [true, false]})
120
+ assert_equal(%{{
121
+ "a" : 1,
122
+ "b" : [
123
+ true,
124
+ false
125
+ ]
126
+ }}, json)
127
+ end
128
+ # TBD with options
129
+
130
+ # parse
131
+ def test_parse
132
+ json = %{{"a":1,"b":[true,false]}}
133
+ obj = JSON.parse(json)
134
+ assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
135
+ end
136
+ def test_parse_sym_names
137
+ json = %{{"a":1,"b":[true,false]}}
138
+ obj = JSON.parse(json, :symbolize_names => true)
139
+ assert_equal({ :a => 1, :b => [true, false]}, obj)
140
+ end
141
+ def test_parse_additions
142
+ jam = Jam.new(true, 58)
143
+ json = Oj.dump(jam, :mode => :compat)
144
+ obj = JSON.parse(json)
145
+ assert_equal(jam, obj)
146
+ obj = JSON.parse(json, :create_additions => true)
147
+ assert_equal(jam, obj)
148
+ obj = JSON.parse(json, :create_additions => false)
149
+ assert_equal({'json_class' => 'Jam', 'x' => true, 'y' => 58}, obj)
150
+ end
151
+ def test_parse_bang
152
+ json = %{{"a":1,"b":[true,false]}}
153
+ obj = JSON.parse!(json)
154
+ assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
155
+ end
156
+
157
+ # recurse_proc
158
+ def test_recurse_proc
159
+ children = []
160
+ obj = JSON.recurse_proc({ 'a' => 1, 'b' => [true, false]}) { |x| children << x }
161
+ assert_equal([1, true, false, [true, false], { 'a' => 1, 'b' => [true, false]}], children)
162
+ end
163
+
164
+ end # Mimic
@@ -129,6 +129,8 @@ class Juice < ::Test::Unit::TestCase
129
129
  dump_and_load(12345.6789, false)
130
130
  dump_and_load(-54321.012, false)
131
131
  dump_and_load(2.48e16, false)
132
+ dump_and_load(2.48e1000, false)
133
+ dump_and_load(-2.48e1000, false)
132
134
  end
133
135
 
134
136
  def test_string
@@ -481,7 +483,7 @@ class Juice < ::Test::Unit::TestCase
481
483
  end
482
484
 
483
485
  # Stream IO
484
- def test_string_io
486
+ def test_io_string
485
487
  json = %{{
486
488
  "x":true,
487
489
  "y":58,
@@ -493,6 +495,21 @@ class Juice < ::Test::Unit::TestCase
493
495
  assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
494
496
  end
495
497
 
498
+ def test_io_file
499
+ filename = 'open_file_test.json'
500
+ File.open(filename, 'w') { |f| f.write(%{{
501
+ "x":true,
502
+ "y":58,
503
+ "z": [1,2,3]
504
+ }
505
+ }) }
506
+ f = File.new(filename)
507
+ obj = Oj.load(f, :mode => :strict)
508
+ f.close()
509
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
510
+ end
511
+
512
+ # symbol_keys option
496
513
  def test_symbol_keys
497
514
  json = %{{
498
515
  "x":true,
@@ -504,6 +521,42 @@ class Juice < ::Test::Unit::TestCase
504
521
  assert_equal({ :x => true, :y => 58, :z => [1, 2, 3]}, obj)
505
522
  end
506
523
 
524
+ # comments
525
+ def test_comment_slash
526
+ json = %{{
527
+ "x":true,//three
528
+ "y":58,
529
+ "z": [1,2,
530
+ 3 // six
531
+ ]}
532
+ }
533
+ obj = Oj.load(json, :mode => :strict)
534
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
535
+ end
536
+
537
+ def test_comment_c
538
+ json = %{{
539
+ "x"/*one*/:/*two*/true,
540
+ "y":58,
541
+ "z": [1,2,3]}
542
+ }
543
+ obj = Oj.load(json, :mode => :strict)
544
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
545
+ end
546
+
547
+ def test_comment
548
+ json = %{{
549
+ "x"/*one*/:/*two*/true,//three
550
+ "y":58/*four*/,
551
+ "z": [1,2/*five*/,
552
+ 3 // six
553
+ ]
554
+ }
555
+ }
556
+ obj = Oj.load(json, :mode => :strict)
557
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
558
+ end
559
+
507
560
  def dump_and_load(obj, trace=false)
508
561
  json = Oj.dump(obj, :indent => 2)
509
562
  puts json if trace