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.
- checksums.yaml +4 -4
- data/README.md +6 -4
- data/ext/oj/dump.c +97 -70
- data/ext/oj/extconf.rb +1 -2
- data/ext/oj/object.c +11 -11
- data/ext/oj/oj.c +64 -7
- data/ext/oj/oj.h +8 -1
- data/ext/oj/parse.c +11 -18
- data/ext/oj/saj.c +7 -13
- data/ext/oj/scp.c +4 -4
- data/ext/oj/strict.c +4 -4
- data/ext/oj/val_stack.c +30 -0
- data/ext/oj/val_stack.h +23 -28
- data/lib/oj/version.rb +1 -1
- data/test/test_gc.rb +51 -0
- data/test/test_saj.rb +1 -1
- data/test/tests.rb +49 -19
- metadata +3 -2
data/ext/oj/strict.c
CHANGED
@@ -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);
|
data/ext/oj/val_stack.c
CHANGED
@@ -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) {
|
data/ext/oj/val_stack.h
CHANGED
@@ -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
|
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
|
54
|
-
const char
|
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
|
60
|
-
uint16_t
|
61
|
-
char
|
62
|
-
char
|
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
|
67
|
-
Val
|
68
|
-
Val
|
69
|
-
Val
|
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
|
-
|
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
|
-
|
106
|
-
memcpy(
|
98
|
+
head = ALLOC_N(struct _Val, len + STACK_INC);
|
99
|
+
memcpy(head, stack->base, sizeof(struct _Val) * len);
|
107
100
|
} else {
|
108
|
-
REALLOC_N(
|
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
|
|
data/lib/oj/version.rb
CHANGED
data/test/test_gc.rb
ADDED
@@ -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
|
data/test/test_saj.rb
CHANGED
@@ -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:
|
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
|
data/test/tests.rb
CHANGED
@@ -40,13 +40,13 @@ class Jam
|
|
40
40
|
end
|
41
41
|
alias == eql?
|
42
42
|
|
43
|
-
end
|
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
|
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
|
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
|
-
:
|
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
|
-
:
|
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
|
-
:
|
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.
|
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-
|
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
|