oj 3.13.7 → 3.13.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/ext/oj/fast.c +6 -2
- data/ext/oj/intern.c +7 -1
- data/ext/oj/object.c +26 -45
- data/ext/oj/parse.c +9 -1
- data/ext/oj/usual.c +4 -2
- data/lib/oj/version.rb +1 -1
- data/test/bug.rb +16 -0
- data/test/foo.rb +9 -8
- data/test/test_fast.rb +18 -7
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 182e77505f61b9f1327d36552a04ac18a960831d01d675b02ec00ec6e33d3239
|
4
|
+
data.tar.gz: 4b56206268f665fe45a8cbd63b605bab35a0adf834e2ad1d603d4e99a1d8b118
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc57186d14210b95b7875948ceb2e1782f7a3223cfabcf7e45a5cad617eba67fe506bfe17820decf1881d341e6233c114f617c3ad8395033d75c7be8b72804b9
|
7
|
+
data.tar.gz: cc069dec094cde64f098fa65131d81f6552f2302af87730e19442ad027ad5722f7d6555de1e4a888196b994fcbb8f809f3892a10b05afd955b805851e7d53928
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
|
4
|
+
## 3.13.8 - 2021-09-27
|
5
|
+
|
6
|
+
- Fix `Oj::Doc` behaviour for inexisting path.
|
7
|
+
```ruby
|
8
|
+
Oj::Doc.open('{"foo":1}') do |doc|
|
9
|
+
doc.fetch('/foo/bar') # used to give `1`, now gives `nil`
|
10
|
+
doc.exists?('/foo/bar') # used to give `true`, now gives `false`
|
11
|
+
end
|
12
|
+
```
|
13
|
+
|
14
|
+
- Fix `Oj::Parser` handling of BigDecimal. `snprint()` does not handle `%Lg` correctly but `sprintf()` does.
|
15
|
+
|
3
16
|
## 3.13.7 - 2021-09-16
|
4
17
|
|
5
18
|
- The JSON gem allows invalid unicode so Oj, when mimicing JSON now
|
data/ext/oj/fast.c
CHANGED
@@ -879,6 +879,10 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
879
879
|
}
|
880
880
|
} else if (NULL == leaf->elements) {
|
881
881
|
leaf = NULL;
|
882
|
+
} else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
|
883
|
+
// We are trying to get a children of a leaf, which
|
884
|
+
// doesn't exist.
|
885
|
+
leaf = NULL;
|
882
886
|
} else if (COL_VAL == leaf->value_type) {
|
883
887
|
Leaf first = leaf->elements->next;
|
884
888
|
Leaf e = first;
|
@@ -1373,8 +1377,8 @@ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1373
1377
|
* Returns true if the value at the location identified by the path exists.
|
1374
1378
|
* @param [String] path path to the location
|
1375
1379
|
* @example
|
1376
|
-
* Oj::Doc.open('[1,2]') { |doc| doc.exists('/1') } #=> true
|
1377
|
-
* Oj::Doc.open('[1,2]') { |doc| doc.exists('/3') } #=> false
|
1380
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
|
1381
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
|
1378
1382
|
*/
|
1379
1383
|
static VALUE doc_exists(VALUE self, VALUE str) {
|
1380
1384
|
Doc doc;
|
data/ext/oj/intern.c
CHANGED
@@ -186,6 +186,7 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
|
|
186
186
|
char * end = class_name + sizeof(class_name) - 1;
|
187
187
|
char * s;
|
188
188
|
const char *n = name;
|
189
|
+
size_t nlen = len;
|
189
190
|
|
190
191
|
clas = rb_cObject;
|
191
192
|
for (s = class_name; 0 < len; n++, len--) {
|
@@ -208,7 +209,12 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
|
|
208
209
|
}
|
209
210
|
*s = '\0';
|
210
211
|
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
211
|
-
|
212
|
+
if (sizeof(class_name) <= nlen) {
|
213
|
+
nlen = sizeof(class_name) - 1;
|
214
|
+
}
|
215
|
+
strncpy(class_name, name, nlen);
|
216
|
+
class_name[nlen] = '\0';
|
217
|
+
oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class '%s' is not defined", class_name);
|
212
218
|
if (Qnil != error_class) {
|
213
219
|
pi->err_class = error_class;
|
214
220
|
}
|
data/ext/oj/object.c
CHANGED
@@ -35,7 +35,7 @@ static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
|
|
35
35
|
return ID2SYM(rb_intern3(kval->key + 1, kval->klen - 1, oj_utf8_encoding));
|
36
36
|
}
|
37
37
|
if (Yes == pi->options.sym_key) {
|
38
|
-
|
38
|
+
return ID2SYM(rb_intern3(kval->key, kval->klen, oj_utf8_encoding));
|
39
39
|
}
|
40
40
|
#if HAVE_RB_ENC_INTERNED_STR
|
41
41
|
rkey = rb_enc_interned_str(kval->key, kval->klen, oj_utf8_encoding);
|
@@ -60,21 +60,16 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
|
|
60
60
|
}
|
61
61
|
rstr = oj_circ_array_get(pi->circ_array, i);
|
62
62
|
} else {
|
63
|
-
|
63
|
+
rstr = rb_utf8_str_new(str, len);
|
64
64
|
}
|
65
65
|
return rstr;
|
66
66
|
}
|
67
67
|
|
68
|
-
#if (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
|
69
|
-
static VALUE oj_parse_xml_time(const char *str, int len) {
|
70
|
-
return rb_funcall(rb_cTime, oj_parse_id, 1, rb_str_new(str, len));
|
71
|
-
}
|
72
|
-
#else
|
73
68
|
// The much faster approach (4x faster)
|
74
69
|
static int parse_num(const char *str, const char *end, int cnt) {
|
75
|
-
int
|
70
|
+
int n = 0;
|
76
71
|
char c;
|
77
|
-
int
|
72
|
+
int i;
|
78
73
|
|
79
74
|
for (i = cnt; 0 < i; i--, str++) {
|
80
75
|
c = *str;
|
@@ -88,9 +83,9 @@ static int parse_num(const char *str, const char *end, int cnt) {
|
|
88
83
|
|
89
84
|
VALUE
|
90
85
|
oj_parse_xml_time(const char *str, int len) {
|
91
|
-
VALUE
|
86
|
+
VALUE args[8];
|
92
87
|
const char *end = str + len;
|
93
|
-
int
|
88
|
+
int n;
|
94
89
|
|
95
90
|
// year
|
96
91
|
if (0 > (n = parse_num(str, end, 4))) {
|
@@ -201,7 +196,6 @@ oj_parse_xml_time(const char *str, int len) {
|
|
201
196
|
}
|
202
197
|
return rb_funcall2(rb_cTime, oj_new_id, 7, args);
|
203
198
|
}
|
204
|
-
#endif
|
205
199
|
|
206
200
|
static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t len) {
|
207
201
|
const char *key = kval->key;
|
@@ -226,13 +220,10 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
|
|
226
220
|
}
|
227
221
|
parent->val = odd->clas;
|
228
222
|
parent->odd_args = oj_odd_alloc_args(odd);
|
229
|
-
} break;
|
230
|
-
case 'm':
|
231
|
-
parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
|
232
|
-
break;
|
233
|
-
case 's':
|
234
|
-
parent->val = rb_utf8_str_new(str, len);
|
235
223
|
break;
|
224
|
+
}
|
225
|
+
case 'm': parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding)); break;
|
226
|
+
case 's': parent->val = rb_utf8_str_new(str, len); break;
|
236
227
|
case 'c': // class
|
237
228
|
{
|
238
229
|
VALUE clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
|
@@ -242,7 +233,8 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
|
|
242
233
|
} else {
|
243
234
|
parent->val = clas;
|
244
235
|
}
|
245
|
-
|
236
|
+
break;
|
237
|
+
}
|
246
238
|
case 't': // time
|
247
239
|
parent->val = oj_parse_xml_time(str, (int)len);
|
248
240
|
break;
|
@@ -282,22 +274,21 @@ static int hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
|
|
282
274
|
VALUE args[8];
|
283
275
|
|
284
276
|
sec_as_time(t, &ti);
|
285
|
-
args[0]
|
286
|
-
args[1]
|
287
|
-
args[2]
|
288
|
-
args[3]
|
289
|
-
args[4]
|
290
|
-
args[5]
|
291
|
-
args[6]
|
277
|
+
args[0] = LONG2NUM((long)(ti.year));
|
278
|
+
args[1] = LONG2NUM(ti.mon);
|
279
|
+
args[2] = LONG2NUM(ti.day);
|
280
|
+
args[3] = LONG2NUM(ti.hour);
|
281
|
+
args[4] = LONG2NUM(ti.min);
|
282
|
+
args[5] = rb_float_new((double)ti.sec + ((double)nsec + 0.5) / 1000000000.0);
|
283
|
+
args[6] = LONG2NUM(ni->exp);
|
292
284
|
parent->val = rb_funcall2(rb_cTime, oj_new_id, 7, args);
|
293
285
|
} else {
|
294
286
|
parent->val = rb_time_nano_new(ni->i, (long)nsec);
|
295
287
|
}
|
296
288
|
}
|
297
289
|
break;
|
298
|
-
case 'i':
|
299
|
-
if (!ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp &&
|
300
|
-
0 != pi->circ_array) { // fixnum
|
290
|
+
case 'i': // circular index
|
291
|
+
if (!ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
|
301
292
|
if (Qnil == parent->val) {
|
302
293
|
parent->val = rb_hash_new();
|
303
294
|
}
|
@@ -402,9 +393,7 @@ WHICH_TYPE:
|
|
402
393
|
}
|
403
394
|
break;
|
404
395
|
case T_HASH:
|
405
|
-
rb_hash_aset(parent->val,
|
406
|
-
calc_hash_key(pi, kval, parent->k1),
|
407
|
-
str_to_value(pi, str, len, orig));
|
396
|
+
rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), str_to_value(pi, str, len, orig));
|
408
397
|
break;
|
409
398
|
case T_STRING:
|
410
399
|
rval = str_to_value(pi, str, len, orig);
|
@@ -481,8 +470,8 @@ WHICH_TYPE:
|
|
481
470
|
rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), rval);
|
482
471
|
break;
|
483
472
|
case T_OBJECT:
|
484
|
-
if (2 == klen && '^' == *key && 'i' == key[1] && !ni->infinity && !ni->neg &&
|
485
|
-
|
473
|
+
if (2 == klen && '^' == *key && 'i' == key[1] && !ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp &&
|
474
|
+
0 != pi->circ_array) { // fixnum
|
486
475
|
oj_circ_array_set(pi->circ_array, parent->val, ni->i);
|
487
476
|
} else {
|
488
477
|
rval = oj_num_as_value(ni);
|
@@ -559,11 +548,7 @@ WHICH_TYPE:
|
|
559
548
|
volatile VALUE *a = RARRAY_PTR(value);
|
560
549
|
|
561
550
|
if (2 != len) {
|
562
|
-
oj_set_error_at(pi,
|
563
|
-
oj_parse_error_class,
|
564
|
-
__FILE__,
|
565
|
-
__LINE__,
|
566
|
-
"invalid hash pair");
|
551
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
|
567
552
|
return;
|
568
553
|
}
|
569
554
|
rb_hash_aset(parent->val, *a, a[1]);
|
@@ -637,10 +622,7 @@ static void end_hash(ParseInfo pi) {
|
|
637
622
|
} else if (NULL != parent->odd_args) {
|
638
623
|
OddArgs oa = parent->odd_args;
|
639
624
|
|
640
|
-
parent->val = rb_funcall2(oa->odd->create_obj,
|
641
|
-
oa->odd->create_op,
|
642
|
-
oa->odd->attr_cnt,
|
643
|
-
oa->args);
|
625
|
+
parent->val = rb_funcall2(oa->odd->create_obj, oa->odd->create_op, oa->odd->attr_cnt, oa->args);
|
644
626
|
oj_odd_free(oa);
|
645
627
|
parent->odd_args = NULL;
|
646
628
|
}
|
@@ -653,8 +635,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
|
|
653
635
|
volatile VALUE rval = Qnil;
|
654
636
|
|
655
637
|
// orig lets us know whether the string was ^r1 or \u005er1
|
656
|
-
if (3 <= len && 0 != pi->circ_array && '^' == orig[0] &&
|
657
|
-
0 == rb_array_len(stack_peek(&pi->stack)->val)) {
|
638
|
+
if (3 <= len && 0 != pi->circ_array && '^' == orig[0] && 0 == rb_array_len(stack_peek(&pi->stack)->val)) {
|
658
639
|
if ('i' == str[1]) {
|
659
640
|
long i = read_long(str + 2, len - 2);
|
660
641
|
|
data/ext/oj/parse.c
CHANGED
@@ -904,9 +904,17 @@ void oj_set_error_at(ParseInfo pi,
|
|
904
904
|
char * end = p + sizeof(msg) - 2;
|
905
905
|
char * start;
|
906
906
|
Val vp;
|
907
|
+
int mlen;
|
907
908
|
|
908
909
|
va_start(ap, format);
|
909
|
-
|
910
|
+
mlen = vsnprintf(msg, sizeof(msg) - 1, format, ap);
|
911
|
+
if (0 < mlen) {
|
912
|
+
if (sizeof(msg) - 2 < (size_t)mlen) {
|
913
|
+
p = end - 2;
|
914
|
+
} else {
|
915
|
+
p += mlen;
|
916
|
+
}
|
917
|
+
}
|
910
918
|
va_end(ap);
|
911
919
|
pi->err.clas = err_clas;
|
912
920
|
if (p + 3 < end) {
|
data/ext/oj/usual.c
CHANGED
@@ -537,7 +537,7 @@ static void add_float_key(ojParser p) {
|
|
537
537
|
static void add_float_as_big(ojParser p) {
|
538
538
|
char buf[64];
|
539
539
|
|
540
|
-
// fails on ubuntu
|
540
|
+
// snprintf fails on ubuntu and macOS for long double
|
541
541
|
// snprintf(buf, sizeof(buf), "%Lg", p->num.dub);
|
542
542
|
sprintf(buf, "%Lg", p->num.dub);
|
543
543
|
push(p, rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new2(buf)));
|
@@ -546,7 +546,9 @@ static void add_float_as_big(ojParser p) {
|
|
546
546
|
static void add_float_as_big_key(ojParser p) {
|
547
547
|
char buf[64];
|
548
548
|
|
549
|
-
snprintf
|
549
|
+
// snprintf fails on ubuntu and macOS for long double
|
550
|
+
// snprintf(buf, sizeof(buf), "%Lg", p->num.dub);
|
551
|
+
sprintf(buf, "%Lg", p->num.dub);
|
550
552
|
push_key(p);
|
551
553
|
push2(p, rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new2(buf)));
|
552
554
|
}
|
data/lib/oj/version.rb
CHANGED
data/test/bug.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$: << '.'
|
2
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
3
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
4
|
+
|
5
|
+
|
6
|
+
#require 'bundler/setup'
|
7
|
+
require 'oj'
|
8
|
+
require 'active_support'
|
9
|
+
require 'active_support/time_with_zone'
|
10
|
+
require 'tzinfo'
|
11
|
+
|
12
|
+
puts ActiveSupport::TimeWithZone
|
13
|
+
|
14
|
+
json = File.read('./bug.json')
|
15
|
+
|
16
|
+
Oj.load(json)
|
data/test/foo.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
$: << '.'
|
4
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
5
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
6
6
|
|
7
|
-
|
8
|
-
puts "Oj version: #{Oj::VERSION}"
|
7
|
+
require 'oj'
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
parser = Oj::Parser.new(:usual, cache_keys: true, cache_strings: 30, decimal: :bigdecimal, symbol_keys: false)
|
10
|
+
raw = %({"amount":0.10})
|
11
|
+
parsed = parser.parse(raw)
|
12
12
|
|
13
|
-
|
13
|
+
pp parsed
|
14
|
+
puts parsed['amount'].class
|
data/test/test_fast.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
$: << File.dirname(__FILE__)
|
5
5
|
|
@@ -36,6 +36,17 @@ class DocTest < Minitest::Test
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
def test_leaf_of_existing_path
|
40
|
+
json = %{{"foo": 1, "fizz": true}}
|
41
|
+
Oj::Doc.open(json) do |doc|
|
42
|
+
%w(/foo/bar /fizz/bar).each do |path|
|
43
|
+
assert_nil(doc.fetch(path))
|
44
|
+
assert_equal(:default, doc.fetch(path, :default))
|
45
|
+
refute(doc.exists?(path))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
39
50
|
def test_true
|
40
51
|
json = %{true}
|
41
52
|
Oj::Doc.open(json) do |doc|
|
@@ -282,11 +293,11 @@ class DocTest < Minitest::Test
|
|
282
293
|
['/nothing', nil],
|
283
294
|
['/array/10', nil],
|
284
295
|
].each do |path,val|
|
285
|
-
|
286
|
-
|
287
|
-
|
296
|
+
if val.nil?
|
297
|
+
assert_nil(doc.fetch(path))
|
298
|
+
else
|
288
299
|
assert_equal(val, doc.fetch(path))
|
289
|
-
|
300
|
+
end
|
290
301
|
end
|
291
302
|
end
|
292
303
|
# verify empty hash and arrays return nil when a member is requested
|
@@ -313,7 +324,7 @@ class DocTest < Minitest::Test
|
|
313
324
|
end
|
314
325
|
end
|
315
326
|
|
316
|
-
def
|
327
|
+
def test_exists
|
317
328
|
Oj::Doc.open(@json1) do |doc|
|
318
329
|
[['/array/1', true],
|
319
330
|
['/array/1', true],
|
@@ -322,7 +333,7 @@ class DocTest < Minitest::Test
|
|
322
333
|
['/array/3', false],
|
323
334
|
['/nothing', false],
|
324
335
|
].each do |path,val|
|
325
|
-
assert_equal(val, doc.exists?(path))
|
336
|
+
assert_equal(val, doc.exists?(path), "failed for #{path.inspect}")
|
326
337
|
end
|
327
338
|
end
|
328
339
|
end
|
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: 3.13.
|
4
|
+
version: 3.13.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -201,6 +201,7 @@ files:
|
|
201
201
|
- test/activesupport6/time_zone_test_helpers.rb
|
202
202
|
- test/bar.rb
|
203
203
|
- test/baz.rb
|
204
|
+
- test/bug.rb
|
204
205
|
- test/files.rb
|
205
206
|
- test/foo.rb
|
206
207
|
- test/helper.rb
|
@@ -332,6 +333,7 @@ test_files:
|
|
332
333
|
- test/activesupport6/time_zone_test_helpers.rb
|
333
334
|
- test/bar.rb
|
334
335
|
- test/baz.rb
|
336
|
+
- test/bug.rb
|
335
337
|
- test/files.rb
|
336
338
|
- test/foo.rb
|
337
339
|
- test/helper.rb
|