oj 2.1.7 → 2.2.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.

@@ -52,7 +52,7 @@ add_value(ParseInfo pi, VALUE val) {
52
52
 
53
53
  static void
54
54
  add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
55
- VALUE rstr = rb_str_new(str, len);
55
+ volatile VALUE rstr = rb_str_new(str, len);
56
56
 
57
57
  rstr = oj_encode(rstr);
58
58
  pi->stack.head->val = rstr;
@@ -70,7 +70,7 @@ start_hash(ParseInfo pi) {
70
70
 
71
71
  static VALUE
72
72
  hash_key(ParseInfo pi, const char *key, size_t klen) {
73
- VALUE rkey = rb_str_new(key, klen);
73
+ volatile VALUE rkey = rb_str_new(key, klen);
74
74
 
75
75
  rkey = oj_encode(rkey);
76
76
  if (Yes == pi->options.sym_key) {
@@ -81,7 +81,7 @@ hash_key(ParseInfo pi, const char *key, size_t klen) {
81
81
 
82
82
  static void
83
83
  hash_set_cstr(ParseInfo pi, const char *key, size_t klen, const char *str, size_t len, const char *orig) {
84
- VALUE rstr = rb_str_new(str, len);
84
+ volatile VALUE rstr = rb_str_new(str, len);
85
85
 
86
86
  rstr = oj_encode(rstr);
87
87
  rb_hash_aset(stack_peek(&pi->stack)->val, hash_key(pi, key, klen), rstr);
@@ -104,7 +104,7 @@ start_array(ParseInfo pi) {
104
104
 
105
105
  static void
106
106
  array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
107
- VALUE rstr = rb_str_new(str, len);
107
+ volatile VALUE rstr = rb_str_new(str, len);
108
108
 
109
109
  rstr = oj_encode(rstr);
110
110
  rb_ary_push(stack_peek(&pi->stack)->val, rstr);
@@ -28,8 +28,38 @@
28
28
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
  */
30
30
 
31
+ #include "oj.h"
31
32
  #include "val_stack.h"
32
33
 
34
+ static void
35
+ mark(void *ptr) {
36
+ ValStack stack = (ValStack)ptr;
37
+ Val v;
38
+
39
+ pthread_mutex_lock(&stack->mutex);
40
+ for (v = stack->head; v < stack->tail; v++) {
41
+ if (Qnil != v->val && Qundef != v->val) {
42
+ rb_gc_mark(v->val);
43
+ }
44
+ }
45
+ pthread_mutex_unlock(&stack->mutex);
46
+ }
47
+
48
+ VALUE
49
+ oj_stack_init(ValStack stack) {
50
+ pthread_mutex_init(&stack->mutex, 0);
51
+ stack->head = stack->base;
52
+ stack->end = stack->base + sizeof(stack->base) / sizeof(struct _Val);
53
+ stack->tail = stack->head;
54
+ stack->head->val = Qundef;
55
+ stack->head->key = 0;
56
+ stack->head->classname = 0;
57
+ stack->head->klen = 0;
58
+ stack->head->clen = 0;
59
+ stack->head->next = NEXT_NONE;
60
+ return Data_Wrap_Struct(oj_cstack_class, mark, 0, stack);
61
+ }
62
+
33
63
  const char*
34
64
  oj_stack_next_string(ValNext n) {
35
65
  switch (n) {
@@ -34,8 +34,9 @@
34
34
  #include "ruby.h"
35
35
  #include "odd.h"
36
36
  #include <stdint.h>
37
+ #include <pthread.h>
37
38
 
38
- #define STACK_INC 32
39
+ #define STACK_INC 64
39
40
 
40
41
  typedef enum {
41
42
  NEXT_NONE = 0,
@@ -50,38 +51,27 @@ typedef enum {
50
51
  } ValNext;
51
52
 
52
53
  typedef struct _Val {
53
- VALUE val;
54
- const char *key;
54
+ VALUE val;
55
+ const char *key;
55
56
  union {
56
57
  const char *classname;
57
58
  OddArgs odd_args;
58
59
  };
59
- uint16_t klen;
60
- uint16_t clen;
61
- char next; // ValNext
62
- char k1; // first original character in the key
60
+ uint16_t klen;
61
+ uint16_t clen;
62
+ char next; // ValNext
63
+ char k1; // first original character in the key
63
64
  } *Val;
64
65
 
65
66
  typedef struct _ValStack {
66
- struct _Val base[STACK_INC];
67
- Val head; // current stack
68
- Val end; // stack end
69
- Val tail; // pointer to one past last element name on stack
67
+ struct _Val base[STACK_INC];
68
+ Val head; // current stack
69
+ Val end; // stack end
70
+ Val tail; // pointer to one past last element name on stack
71
+ pthread_mutex_t mutex;
70
72
  } *ValStack;
71
73
 
72
- inline static void
73
- stack_init(ValStack stack) {
74
- stack->head = stack->base;
75
- stack->end = stack->base + sizeof(stack->base) / sizeof(struct _Val);
76
- stack->tail = stack->head;
77
- stack->head->val = Qundef;
78
- stack->head->key = 0;
79
- stack->head->classname = 0;
80
- stack->head->klen = 0;
81
- stack->head->clen = 0;
82
- stack->head->next = NEXT_NONE;
83
- //stack->head->type = TYPE_NONE;
84
- }
74
+ extern VALUE oj_stack_init(ValStack stack);
85
75
 
86
76
  inline static int
87
77
  stack_empty(ValStack stack) {
@@ -100,15 +90,21 @@ stack_push(ValStack stack, VALUE val, ValNext next) {
100
90
  if (stack->end <= stack->tail) {
101
91
  size_t len = stack->end - stack->head;
102
92
  size_t toff = stack->tail - stack->head;
93
+ Val head = stack->head;
103
94
 
95
+ // A realloc can trigger a GC so make sure it happens outside the lock
96
+ // but lock before changing pointers.
104
97
  if (stack->base == stack->head) {
105
- stack->head = ALLOC_N(struct _Val, len + STACK_INC);
106
- memcpy(stack->head, stack->base, sizeof(struct _Val) * len);
98
+ head = ALLOC_N(struct _Val, len + STACK_INC);
99
+ memcpy(head, stack->base, sizeof(struct _Val) * len);
107
100
  } else {
108
- REALLOC_N(stack->head, struct _Val, len + STACK_INC);
101
+ REALLOC_N(head, struct _Val, len + STACK_INC);
109
102
  }
103
+ pthread_mutex_lock(&stack->mutex);
104
+ stack->head = head;
110
105
  stack->tail = stack->head + toff;
111
106
  stack->end = stack->head + len + STACK_INC;
107
+ pthread_mutex_unlock(&stack->mutex);
112
108
  }
113
109
  stack->tail->val = val;
114
110
  stack->tail->next = next;
@@ -116,7 +112,6 @@ stack_push(ValStack stack, VALUE val, ValNext next) {
116
112
  stack->tail->key = 0;
117
113
  stack->tail->clen = 0;
118
114
  stack->tail->klen = 0;
119
- //stack->tail->type = TYPE_NONE;
120
115
  stack->tail++;
121
116
  }
122
117
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.1.7'
4
+ VERSION = '2.2.0'
5
5
  end
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get
5
+ # warnings to show up the -w options is required. That can be set in the RUBYOPT
6
+ # environment variable.
7
+ # export RUBYOPT=-w
8
+
9
+ $VERBOSE = true
10
+
11
+ $: << File.join(File.dirname(__FILE__), "../lib")
12
+ $: << File.join(File.dirname(__FILE__), "../ext")
13
+
14
+ require 'test/unit'
15
+ require 'oj'
16
+
17
+ class Goo
18
+ attr_accessor :x, :child
19
+
20
+ def initialize(x, child)
21
+ @x = x
22
+ @child = child
23
+ end
24
+
25
+ def to_hash()
26
+ { 'json_class' => "#{self.class}", 'x' => x, 'child' => child }
27
+ end
28
+
29
+ def self.json_create(h)
30
+ GC.start
31
+ self.new(h['x'], h['child'])
32
+ end
33
+ end # Goo
34
+
35
+ class GCTest < ::Test::Unit::TestCase
36
+
37
+ # if no crash then the GC marking is working
38
+ def test_parse_compat_gc
39
+ g = Goo.new(0, nil)
40
+ 100.times { |i| g = Goo.new(i, g) }
41
+ json = Oj.dump(g, :mode => :compat)
42
+ Oj.compat_load(json)
43
+ end
44
+
45
+ def test_parse_object_gc
46
+ g = Goo.new(0, nil)
47
+ 100.times { |i| g = Goo.new(i, g) }
48
+ json = Oj.dump(g, :mode => :object)
49
+ Oj.object_load(json)
50
+ end
51
+ end
@@ -178,7 +178,7 @@ class SajTest < ::Test::Unit::TestCase
178
178
  json = %{12345xyz}
179
179
  Oj.saj_parse(handler, json)
180
180
  assert_equal([[:add_value, 12345, nil],
181
- [:error, "invalid format, extra characters at line 1, column 6 [saj.c:711]", 1, 6]], handler.calls)
181
+ [:error, "invalid format, extra characters at line 1, column 6 [saj.c:705]", 1, 6]], handler.calls)
182
182
  end
183
183
 
184
184
  end
@@ -40,13 +40,13 @@ class Jam
40
40
  end
41
41
  alias == eql?
42
42
 
43
- end # Jam
43
+ end# Jam
44
44
 
45
45
  class Jeez < Jam
46
46
  def initialize(x, y)
47
47
  super
48
48
  end
49
-
49
+
50
50
  def to_json()
51
51
  %{{"json_class":"#{self.class}","x":#{@x},"y":#{@y}}}
52
52
  end
@@ -54,7 +54,7 @@ class Jeez < Jam
54
54
  def self.json_create(h)
55
55
  self.new(h['x'], h['y'])
56
56
  end
57
- end # Jeez
57
+ end# Jeez
58
58
 
59
59
  # contributed by sauliusg to fix as_json
60
60
  class Orange < Jam
@@ -97,7 +97,7 @@ class Jazz < Jam
97
97
  def self.json_create(h)
98
98
  self.new(h['x'], h['y'])
99
99
  end
100
- end # Jazz
100
+ end# Jazz
101
101
 
102
102
  class Range
103
103
  def to_hash()
@@ -129,7 +129,7 @@ class Juice < ::Test::Unit::TestCase
129
129
  :auto_define=>false,
130
130
  :symbol_keys=>false,
131
131
  :class_cache=>true,
132
- :ascii_only=>false,
132
+ :escape_mode=>:json,
133
133
  :mode=>:object,
134
134
  :time_format=>:unix,
135
135
  :bigdecimal_as_decimal=>true,
@@ -145,7 +145,7 @@ class Juice < ::Test::Unit::TestCase
145
145
  :auto_define=>false,
146
146
  :symbol_keys=>false,
147
147
  :class_cache=>true,
148
- :ascii_only=>false,
148
+ :escape_mode=>:ascii,
149
149
  :mode=>:object,
150
150
  :time_format=>:unix,
151
151
  :bigdecimal_as_decimal=>true,
@@ -158,7 +158,7 @@ class Juice < ::Test::Unit::TestCase
158
158
  :auto_define=>true,
159
159
  :symbol_keys=>true,
160
160
  :class_cache=>false,
161
- :ascii_only=>true,
161
+ :escape_mode=>:json,
162
162
  :mode=>:compat,
163
163
  :time_format=>:ruby,
164
164
  :bigdecimal_as_decimal=>false,
@@ -250,6 +250,36 @@ class Juice < ::Test::Unit::TestCase
250
250
  dump_and_load([[nil], 58], false)
251
251
  end
252
252
 
253
+ # rails encoding tests
254
+ def test_does_not_escape_entities_by_default
255
+ # use Oj to create the hash since some Rubies don't deal nicely with unicode.
256
+ json = %{{"key":"I <3 this\\u2028space"}}
257
+ hash = Oj.load(json)
258
+ out = Oj.dump(hash)
259
+ assert_equal(json, out)
260
+ end
261
+ def test_escapes_entities_by_default_when_configured_to_do_so
262
+ hash = {'key' => "I <3 this"}
263
+ Oj.default_options = {:escape_mode => :xss_safe}
264
+ out = Oj.dump hash
265
+ assert_equal(%{{"key":"I \\u003c3 this"}}, out)
266
+ end
267
+ def test_escapes_entities_when_asked_to
268
+ hash = {'key' => "I <3 this"}
269
+ out = Oj.dump(hash, :escape_mode => :xss_safe)
270
+ assert_equal(%{{"key":"I \\u003c3 this"}}, out)
271
+ end
272
+ def test_does_not_escape_entities_when_not_asked_to
273
+ hash = {'key' => "I <3 this"}
274
+ out = Oj.dump(hash, :escape_mode => :json)
275
+ assert_equal(%{{"key":"I <3 this"}}, out)
276
+ end
277
+ def test_escapes_common_xss_vectors
278
+ hash = {'key' => "<script>alert(123) && formatHD()</script>"}
279
+ out = Oj.dump(hash, :escape_mode => :xss_safe)
280
+ assert_equal(%{{"key":"\\u003cscript\\u003ealert(123) \\u0026\\u0026 formatHD()\\u003c\\/script\\u003e"}}, out)
281
+ end
282
+
253
283
  # Symbol
254
284
  def test_symbol_strict
255
285
  begin
@@ -267,7 +297,7 @@ class Juice < ::Test::Unit::TestCase
267
297
  def test_symbol_compat
268
298
  json = Oj.dump(:abc, :mode => :compat)
269
299
  assert_equal('"abc"', json)
270
- end
300
+ end
271
301
  def test_symbol_object
272
302
  Oj.default_options = { :mode => :object }
273
303
  #dump_and_load(''.to_sym, false)
@@ -295,7 +325,7 @@ class Juice < ::Test::Unit::TestCase
295
325
  #t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
296
326
  json = Oj.dump(t, :mode => :compat)
297
327
  assert_equal(%{1325775487.123456000}, json)
298
- end
328
+ end
299
329
  def test_unix_time_compat_precision
300
330
  t = Time.xmlschema("2012-01-05T23:58:07.123456789+09:00")
301
331
  #t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
@@ -304,23 +334,23 @@ class Juice < ::Test::Unit::TestCase
304
334
  t = Time.xmlschema("2012-01-05T23:58:07.999600+09:00")
305
335
  json = Oj.dump(t, :mode => :compat, :second_precision => 3)
306
336
  assert_equal(%{1325775488.000}, json)
307
- end
337
+ end
308
338
  def test_unix_time_compat_early
309
339
  t = Time.xmlschema("1954-01-05T00:00:00.123456789+00:00")
310
340
  json = Oj.dump(t, :mode => :compat, :second_precision => 5)
311
341
  assert_equal(%{-504575999.87654}, json)
312
- end
342
+ end
313
343
  def test_unix_time_compat_1970
314
344
  t = Time.xmlschema("1970-01-01T00:00:00.123456789+00:00")
315
345
  json = Oj.dump(t, :mode => :compat, :second_precision => 5)
316
346
  assert_equal(%{0.12346}, json)
317
- end
347
+ end
318
348
  def test_ruby_time_compat
319
349
  t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
320
350
  json = Oj.dump(t, :mode => :compat, :time_format => :ruby)
321
351
  #assert_equal(%{"2012-01-05 23:58:07 +0900"}, json)
322
352
  assert_equal(%{"#{t.to_s}"}, json)
323
- end
353
+ end
324
354
  def test_xml_time_compat
325
355
  begin
326
356
  t = Time.new(2012, 1, 5, 23, 58, 7.123456000, 34200)
@@ -339,7 +369,7 @@ class Juice < ::Test::Unit::TestCase
339
369
  end
340
370
  assert_equal(%{"2012-01-05T23:58:07.123456000%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
341
371
  end
342
- end
372
+ end
343
373
  def test_xml_time_compat_no_secs
344
374
  begin
345
375
  t = Time.new(2012, 1, 5, 23, 58, 7.0, 34200)
@@ -358,7 +388,7 @@ class Juice < ::Test::Unit::TestCase
358
388
  end
359
389
  assert_equal(%{"2012-01-05T23:58:07%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
360
390
  end
361
- end
391
+ end
362
392
  def test_xml_time_compat_precision
363
393
  begin
364
394
  t = Time.new(2012, 1, 5, 23, 58, 7.123456789, 32400)
@@ -377,7 +407,7 @@ class Juice < ::Test::Unit::TestCase
377
407
  end
378
408
  assert_equal(%{"2012-01-05T23:58:07.12346%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
379
409
  end
380
- end
410
+ end
381
411
  def test_xml_time_compat_precision_round
382
412
  begin
383
413
  t = Time.new(2012, 1, 5, 23, 58, 7.9996, 32400)
@@ -396,7 +426,7 @@ class Juice < ::Test::Unit::TestCase
396
426
  end
397
427
  assert_equal(%{"2012-01-05T23:58:08%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
398
428
  end
399
- end
429
+ end
400
430
  def test_xml_time_compat_zulu
401
431
  begin
402
432
  t = Time.new(2012, 1, 5, 23, 58, 7.0, 0)
@@ -409,7 +439,7 @@ class Juice < ::Test::Unit::TestCase
409
439
  #tz = t.utc_offset
410
440
  assert_equal(%{"2012-01-05T23:58:07Z"}, json)
411
441
  end
412
- end
442
+ end
413
443
  def test_time_object
414
444
  t = Time.now()
415
445
  Oj.default_options = { :mode => :object }
@@ -437,7 +467,7 @@ class Juice < ::Test::Unit::TestCase
437
467
  def test_class_compat
438
468
  json = Oj.dump(Juice, :mode => :compat)
439
469
  assert_equal(%{"Juice"}, json)
440
- end
470
+ end
441
471
  def test_class_object
442
472
  Oj.default_options = { :mode => :object }
443
473
  dump_and_load(Juice, false)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.7
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-19 00:00:00.000000000 Z
11
+ date: 2013-11-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'The fastest JSON parser and object serializer. '
14
14
  email: peter@ohler.com
@@ -84,6 +84,7 @@ files:
84
84
  - test/sample_json.rb
85
85
  - test/test_compat.rb
86
86
  - test/test_fast.rb
87
+ - test/test_gc.rb
87
88
  - test/test_mimic.rb
88
89
  - test/test_mimic_after.rb
89
90
  - test/test_object.rb