oj 3.17.7 → 3.17.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +54 -0
- data/ext/oj/cache.c +64 -47
- data/ext/oj/cache.h +1 -1
- data/ext/oj/code.c +38 -28
- data/ext/oj/code.h +2 -0
- data/ext/oj/compat.c +29 -20
- data/ext/oj/custom.c +22 -5
- data/ext/oj/dump.c +93 -29
- data/ext/oj/dump_compat.c +8 -11
- data/ext/oj/dump_object.c +18 -2
- data/ext/oj/dump_strict.c +2 -1
- data/ext/oj/extconf.rb +0 -1
- data/ext/oj/fast.c +46 -29
- data/ext/oj/intern.c +17 -46
- data/ext/oj/mimic_json.c +4 -28
- data/ext/oj/object.c +21 -2
- data/ext/oj/oj.c +80 -21
- data/ext/oj/oj.h +1 -0
- data/ext/oj/parse.c +53 -3
- data/ext/oj/parse.h +9 -0
- data/ext/oj/parser.c +57 -16
- data/ext/oj/rails.c +8 -3
- data/ext/oj/reader.c +4 -0
- data/ext/oj/reader.h +1 -0
- data/ext/oj/resolve.c +5 -4
- data/ext/oj/resolve.h +9 -0
- data/ext/oj/saj.c +14 -7
- data/ext/oj/saj2.c +44 -28
- data/ext/oj/simd.h +3 -5
- data/ext/oj/sparse.c +7 -0
- data/ext/oj/stream_writer.c +3 -0
- data/ext/oj/string_writer.c +17 -3
- data/ext/oj/usual.c +4 -1
- data/ext/oj/val_stack.c +0 -22
- data/ext/oj/val_stack.h +4 -34
- data/ext/oj/wab.c +3 -0
- data/lib/oj/generic_object.rb +82 -0
- data/lib/oj/json.rb +1 -71
- data/lib/oj/mimic.rb +0 -1
- data/lib/oj/version.rb +1 -1
- metadata +16 -15
data/ext/oj/mimic_json.c
CHANGED
|
@@ -241,12 +241,7 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
|
|
|
241
241
|
// default ::JSON::State argument is passed in. Basically a hack to get
|
|
242
242
|
// around the active support hack so two wrongs make a right this time.
|
|
243
243
|
active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (0 == out.buf) {
|
|
247
|
-
rb_raise(rb_eNoMemError, "Not enough memory.");
|
|
248
|
-
}
|
|
249
|
-
rstr = rb_utf8_str_new_cstr(out.buf);
|
|
244
|
+
rstr = oj_dump_obj_to_str(*argv, &copts, &out, 1, active_hack);
|
|
250
245
|
if (2 <= argc && Qnil != argv[1] && rb_respond_to(argv[1], oj_write_id)) {
|
|
251
246
|
VALUE io = argv[1];
|
|
252
247
|
VALUE args[1];
|
|
@@ -255,9 +250,6 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
|
|
|
255
250
|
rb_funcall2(io, oj_write_id, 1, args);
|
|
256
251
|
rstr = io;
|
|
257
252
|
}
|
|
258
|
-
|
|
259
|
-
oj_out_free(&out);
|
|
260
|
-
|
|
261
253
|
return rstr;
|
|
262
254
|
}
|
|
263
255
|
|
|
@@ -380,7 +372,7 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
|
|
380
372
|
}
|
|
381
373
|
*/
|
|
382
374
|
if (1 < argc) {
|
|
383
|
-
|
|
375
|
+
rstr = oj_dump_obj_to_str(*argv, copts, &out, argc - 1, argv + 1);
|
|
384
376
|
} else {
|
|
385
377
|
VALUE active_hack[1];
|
|
386
378
|
|
|
@@ -390,15 +382,8 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
|
|
390
382
|
oj_define_mimic_json(0, NULL, Qnil);
|
|
391
383
|
}
|
|
392
384
|
active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
if (0 == out.buf) {
|
|
396
|
-
rb_raise(rb_eNoMemError, "Not enough memory.");
|
|
385
|
+
rstr = oj_dump_obj_to_str(*argv, copts, &out, 1, active_hack);
|
|
397
386
|
}
|
|
398
|
-
rstr = rb_utf8_str_new_cstr(out.buf);
|
|
399
|
-
|
|
400
|
-
oj_out_free(&out);
|
|
401
|
-
|
|
402
387
|
return rstr;
|
|
403
388
|
}
|
|
404
389
|
|
|
@@ -748,7 +733,6 @@ static struct _options mimic_object_to_json_options = {0, // indent
|
|
|
748
733
|
|
|
749
734
|
static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
|
|
750
735
|
struct _out out;
|
|
751
|
-
VALUE rstr;
|
|
752
736
|
struct _options copts = oj_default_options;
|
|
753
737
|
|
|
754
738
|
copts.str_rx.head = NULL;
|
|
@@ -765,15 +749,7 @@ static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
|
|
|
765
749
|
// To be strict the mimic_object_to_json_options should be used but people
|
|
766
750
|
// seem to prefer the option of changing that.
|
|
767
751
|
// oj_dump_obj_to_json(self, &mimic_object_to_json_options, &out);
|
|
768
|
-
|
|
769
|
-
if (NULL == out.buf) {
|
|
770
|
-
rb_raise(rb_eNoMemError, "Not enough memory.");
|
|
771
|
-
}
|
|
772
|
-
rstr = rb_utf8_str_new_cstr(out.buf);
|
|
773
|
-
|
|
774
|
-
oj_out_free(&out);
|
|
775
|
-
|
|
776
|
-
return rstr;
|
|
752
|
+
return oj_dump_obj_to_str(self, &copts, &out, argc, argv);
|
|
777
753
|
}
|
|
778
754
|
|
|
779
755
|
/* Document-method: state
|
data/ext/oj/object.c
CHANGED
|
@@ -213,8 +213,12 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
|
|
|
213
213
|
switch (key[1]) {
|
|
214
214
|
case 'o': // object
|
|
215
215
|
{ // name2class sets an error if the class is not found or created
|
|
216
|
-
VALUE clas
|
|
216
|
+
VALUE clas;
|
|
217
217
|
|
|
218
|
+
if (oj_is_backtrace_name(str, len)) {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
|
|
218
222
|
if (Qundef != clas) {
|
|
219
223
|
parent->val = rb_obj_alloc(clas);
|
|
220
224
|
}
|
|
@@ -346,9 +350,23 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
|
|
|
346
350
|
}
|
|
347
351
|
if (sc == rb_cRange) {
|
|
348
352
|
parent->val = rb_class_new_instance((int)(len - 1), RARRAY_CONST_PTR(value) + 1, rb_cRange);
|
|
353
|
+
} else if (Qtrue == rb_class_inherited_p(sc, rb_cRange)) {
|
|
354
|
+
volatile VALUE args = rb_ary_new_from_values((long)len, RARRAY_CONST_PTR(value));
|
|
355
|
+
volatile VALUE init = rb_funcall(rb_cRange,
|
|
356
|
+
rb_intern("instance_method"),
|
|
357
|
+
1,
|
|
358
|
+
ID2SYM(rb_intern("initialize")));
|
|
359
|
+
|
|
360
|
+
parent->val = rb_obj_alloc(sc);
|
|
361
|
+
rb_ary_store(args, 0, parent->val);
|
|
362
|
+
rb_apply(init, rb_intern("bind_call"), args);
|
|
349
363
|
} else {
|
|
350
364
|
// Create a properly initialized struct instance without calling the initialize method.
|
|
351
365
|
parent->val = rb_obj_alloc(sc);
|
|
366
|
+
if (T_STRUCT != rb_type(parent->val)) {
|
|
367
|
+
oj_set_error_at(pi, rb_eArgError, __FILE__, __LINE__, "%s is not a Struct", rb_class2name(sc));
|
|
368
|
+
return 1;
|
|
369
|
+
}
|
|
352
370
|
// If the JSON array has more entries than the struct class allows, we record an error.
|
|
353
371
|
#ifdef RSTRUCT_LEN
|
|
354
372
|
#if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
|
|
@@ -393,7 +411,8 @@ void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
|
|
|
393
411
|
parent->val = rb_funcall(parent->val, rb_intern("exception"), 1, value);
|
|
394
412
|
} else if (kval->klen == 3 && strncmp("~bt", kval->key, 3) == 0 && rb_obj_is_kind_of(parent->val, rb_eException)) {
|
|
395
413
|
rb_funcall(parent->val, rb_intern("set_backtrace"), 1, value);
|
|
396
|
-
} else
|
|
414
|
+
} else if (kval->klen != 13 || strncmp("~bt_locations", kval->key, 13) != 0 ||
|
|
415
|
+
!rb_obj_is_kind_of(parent->val, rb_eException)) {
|
|
397
416
|
rb_ivar_set(parent->val, oj_attr_intern(kval->key, kval->klen), value);
|
|
398
417
|
}
|
|
399
418
|
}
|
data/ext/oj/oj.c
CHANGED
|
@@ -1589,7 +1589,7 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
|
|
|
1589
1589
|
if (1 > argc) {
|
|
1590
1590
|
rb_raise(rb_eArgError, "Wrong number of arguments to load().");
|
|
1591
1591
|
}
|
|
1592
|
-
path =
|
|
1592
|
+
path = StringValueCStr(*argv);
|
|
1593
1593
|
parse_info_init(&pi);
|
|
1594
1594
|
pi.options = oj_default_options;
|
|
1595
1595
|
pi.handler = Qnil;
|
|
@@ -1799,7 +1799,6 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
|
|
|
1799
1799
|
static VALUE to_json(int argc, VALUE *argv, VALUE self) {
|
|
1800
1800
|
struct _out out;
|
|
1801
1801
|
struct _options copts = oj_default_options;
|
|
1802
|
-
VALUE rstr;
|
|
1803
1802
|
|
|
1804
1803
|
if (1 > argc) {
|
|
1805
1804
|
rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
|
|
@@ -1818,16 +1817,71 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
|
|
|
1818
1817
|
out.omit_null_byte = copts.dump_opts.omit_null_byte;
|
|
1819
1818
|
// For obj.to_json or generate nan is not allowed but if called from dump
|
|
1820
1819
|
// it is.
|
|
1821
|
-
|
|
1820
|
+
return oj_dump_obj_to_str(*argv, &copts, &out, argc - 1, argv + 1);
|
|
1821
|
+
}
|
|
1822
1822
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1823
|
+
struct option_buffers {
|
|
1824
|
+
const char *create_id;
|
|
1825
|
+
VALUE *ignore;
|
|
1826
|
+
const char *only;
|
|
1827
|
+
const char *except;
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1830
|
+
struct write_call_arg {
|
|
1831
|
+
Options copts;
|
|
1832
|
+
int argc;
|
|
1833
|
+
VALUE *argv;
|
|
1834
|
+
struct option_buffers base;
|
|
1835
|
+
struct option_buffers own;
|
|
1836
|
+
bool owned;
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
static void option_buffers_get(Options copts, struct option_buffers *bufs) {
|
|
1840
|
+
bufs->create_id = copts->create_id;
|
|
1841
|
+
bufs->ignore = copts->ignore;
|
|
1842
|
+
bufs->only = copts->dump_opts.only;
|
|
1843
|
+
bufs->except = copts->dump_opts.except;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
static void write_call_options(struct write_call_arg *arg) {
|
|
1847
|
+
oj_options_take_ownership(arg->copts);
|
|
1848
|
+
option_buffers_get(arg->copts, &arg->own);
|
|
1849
|
+
arg->owned = true;
|
|
1850
|
+
if (3 == arg->argc) {
|
|
1851
|
+
oj_parse_options(arg->argv[2], arg->copts);
|
|
1825
1852
|
}
|
|
1826
|
-
|
|
1853
|
+
}
|
|
1827
1854
|
|
|
1828
|
-
|
|
1855
|
+
static void free_call_buffer(const void *cur, const void *own, const void *base) {
|
|
1856
|
+
if (NULL != cur && base != cur) {
|
|
1857
|
+
OJ_R_FREE((void *)cur);
|
|
1858
|
+
}
|
|
1859
|
+
if (NULL != own && base != own && cur != own) {
|
|
1860
|
+
OJ_R_FREE((void *)own);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1829
1863
|
|
|
1830
|
-
|
|
1864
|
+
static VALUE write_call_ensure(VALUE a) {
|
|
1865
|
+
struct write_call_arg *arg = (struct write_call_arg *)a;
|
|
1866
|
+
Options copts = arg->copts;
|
|
1867
|
+
struct option_buffers none = {NULL, NULL, NULL, NULL};
|
|
1868
|
+
struct option_buffers *base = arg->owned ? &none : &arg->base;
|
|
1869
|
+
|
|
1870
|
+
free_call_buffer(copts->create_id, arg->own.create_id, base->create_id);
|
|
1871
|
+
free_call_buffer(copts->ignore, arg->own.ignore, base->ignore);
|
|
1872
|
+
free_call_buffer(copts->dump_opts.only, arg->own.only, base->only);
|
|
1873
|
+
free_call_buffer(copts->dump_opts.except, arg->own.except, base->except);
|
|
1874
|
+
|
|
1875
|
+
return Qnil;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
static VALUE to_file_body(VALUE a) {
|
|
1879
|
+
struct write_call_arg *arg = (struct write_call_arg *)a;
|
|
1880
|
+
|
|
1881
|
+
write_call_options(arg);
|
|
1882
|
+
oj_write_obj_to_file(arg->argv[1], StringValueCStr(*arg->argv), arg->copts);
|
|
1883
|
+
|
|
1884
|
+
return Qnil;
|
|
1831
1885
|
}
|
|
1832
1886
|
|
|
1833
1887
|
/* Document-method: to_file
|
|
@@ -1841,13 +1895,21 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
|
|
|
1841
1895
|
* - *:circular* [_Boolean_] allow circular references, default: false
|
|
1842
1896
|
*/
|
|
1843
1897
|
static VALUE to_file(int argc, VALUE *argv, VALUE self) {
|
|
1844
|
-
struct _options
|
|
1898
|
+
struct _options copts;
|
|
1899
|
+
struct write_call_arg arg = {&copts, argc, argv};
|
|
1845
1900
|
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1901
|
+
StringValueCStr(*argv);
|
|
1902
|
+
copts = oj_default_options;
|
|
1903
|
+
option_buffers_get(&copts, &arg.base);
|
|
1904
|
+
|
|
1905
|
+
return rb_ensure(to_file_body, (VALUE)&arg, write_call_ensure, (VALUE)&arg);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
static VALUE to_stream_body(VALUE a) {
|
|
1909
|
+
struct write_call_arg *arg = (struct write_call_arg *)a;
|
|
1910
|
+
|
|
1911
|
+
write_call_options(arg);
|
|
1912
|
+
oj_write_obj_to_stream(arg->argv[1], *arg->argv, arg->copts);
|
|
1851
1913
|
|
|
1852
1914
|
return Qnil;
|
|
1853
1915
|
}
|
|
@@ -1863,15 +1925,12 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
|
|
|
1863
1925
|
* - *:circular* [_Boolean_] allow circular references, default: false
|
|
1864
1926
|
*/
|
|
1865
1927
|
static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
|
|
1866
|
-
struct _options
|
|
1928
|
+
struct _options copts = oj_default_options;
|
|
1929
|
+
struct write_call_arg arg = {&copts, argc, argv};
|
|
1867
1930
|
|
|
1868
|
-
|
|
1869
|
-
oj_parse_options(argv[2], &copts);
|
|
1870
|
-
}
|
|
1871
|
-
oj_write_obj_to_stream(argv[1], *argv, &copts);
|
|
1872
|
-
oj_free_call_options(&copts);
|
|
1931
|
+
option_buffers_get(&copts, &arg.base);
|
|
1873
1932
|
|
|
1874
|
-
return
|
|
1933
|
+
return rb_ensure(to_stream_body, (VALUE)&arg, write_call_ensure, (VALUE)&arg);
|
|
1875
1934
|
}
|
|
1876
1935
|
|
|
1877
1936
|
/* Document-method: register_odd
|
data/ext/oj/oj.h
CHANGED
|
@@ -266,6 +266,7 @@ extern void oj_options_mark(Options copts);
|
|
|
266
266
|
|
|
267
267
|
extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
|
|
268
268
|
extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
|
|
269
|
+
extern VALUE oj_dump_obj_to_str(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
|
|
269
270
|
extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
|
|
270
271
|
extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
|
|
271
272
|
extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
|
data/ext/oj/parse.c
CHANGED
|
@@ -390,6 +390,32 @@ void oj_scanner_init(void) {
|
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
+
// JSON does not allow an unescaped control character (below 0x20) in a
|
|
394
|
+
// string. Like a trailing decimal point, that is only rejected in strict mode
|
|
395
|
+
// and when mimicking the JSON gem.
|
|
396
|
+
static inline bool reject_control_chars(ParseInfo pi) {
|
|
397
|
+
return StrictMode == pi->options.mode || CompatMode == pi->options.mode;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static const char *find_control_char(const char *s, const char *end) {
|
|
401
|
+
// Eight bytes at a time first: a byte below 0x20 sets its high bit in
|
|
402
|
+
// (w - 0x20...) & ~w.
|
|
403
|
+
for (; s + sizeof(uint64_t) <= end; s += sizeof(uint64_t)) {
|
|
404
|
+
uint64_t w;
|
|
405
|
+
|
|
406
|
+
memcpy(&w, s, sizeof(w));
|
|
407
|
+
if (0 != ((w - 0x2020202020202020ULL) & ~w & 0x8080808080808080ULL)) {
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
for (; s < end; s++) {
|
|
412
|
+
if ((unsigned char)*s < 0x20) {
|
|
413
|
+
return s;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return NULL;
|
|
417
|
+
}
|
|
418
|
+
|
|
393
419
|
// entered at /
|
|
394
420
|
static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
395
421
|
struct _buf buf;
|
|
@@ -409,6 +435,16 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
|
409
435
|
buf_cleanup(&buf);
|
|
410
436
|
return;
|
|
411
437
|
}
|
|
438
|
+
if (reject_control_chars(pi)) {
|
|
439
|
+
const char *ctrl = find_control_char(s, scanned);
|
|
440
|
+
|
|
441
|
+
if (NULL != ctrl) {
|
|
442
|
+
pi->cur = ctrl;
|
|
443
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
|
|
444
|
+
buf_cleanup(&buf);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
412
448
|
buf_append_string(&buf, s, (size_t)(scanned - s));
|
|
413
449
|
s = scanned;
|
|
414
450
|
|
|
@@ -543,6 +579,15 @@ static void read_str(ParseInfo pi) {
|
|
|
543
579
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
|
|
544
580
|
return;
|
|
545
581
|
}
|
|
582
|
+
if (reject_control_chars(pi)) {
|
|
583
|
+
const char *ctrl = find_control_char(str, pi->cur);
|
|
584
|
+
|
|
585
|
+
if (NULL != ctrl) {
|
|
586
|
+
pi->cur = ctrl;
|
|
587
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
546
591
|
if ('\\' == *pi->cur) {
|
|
547
592
|
read_escaped_str(pi, str);
|
|
548
593
|
return;
|
|
@@ -1205,17 +1250,20 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
|
1205
1250
|
}
|
|
1206
1251
|
} else {
|
|
1207
1252
|
VALUE clas = rb_obj_class(input);
|
|
1208
|
-
VALUE s;
|
|
1209
1253
|
|
|
1210
1254
|
if (oj_stringio_class == clas) {
|
|
1211
|
-
|
|
1212
|
-
|
|
1255
|
+
input = rb_funcall2(input, oj_string_id, 0, 0);
|
|
1256
|
+
StringValue(input);
|
|
1257
|
+
oj_pi_set_input_str(pi, &input);
|
|
1213
1258
|
#if !IS_WINDOWS
|
|
1214
1259
|
} else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
|
|
1215
1260
|
int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
|
|
1216
1261
|
ssize_t cnt;
|
|
1217
1262
|
size_t len = lseek(fd, 0, SEEK_END);
|
|
1218
1263
|
|
|
1264
|
+
if ((size_t)-1 == len) {
|
|
1265
|
+
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
|
1266
|
+
}
|
|
1219
1267
|
lseek(fd, 0, SEEK_SET);
|
|
1220
1268
|
buf = OJ_R_ALLOC_N(char, len + 1);
|
|
1221
1269
|
pi->json = buf;
|
|
@@ -1342,6 +1390,7 @@ CLEANUP:
|
|
|
1342
1390
|
if (NULL != pi->json) {
|
|
1343
1391
|
msg = rb_str_append(msg, rb_utf8_str_new_cstr(" in '"));
|
|
1344
1392
|
msg = rb_str_append(msg, rb_utf8_str_new_cstr(pi->json));
|
|
1393
|
+
RB_GC_GUARD(input);
|
|
1345
1394
|
}
|
|
1346
1395
|
args[0] = msg;
|
|
1347
1396
|
if (pi->err.clas == oj_parse_error_class) {
|
|
@@ -1381,5 +1430,6 @@ CLEANUP:
|
|
|
1381
1430
|
break;
|
|
1382
1431
|
}
|
|
1383
1432
|
}
|
|
1433
|
+
RB_GC_GUARD(input);
|
|
1384
1434
|
return result;
|
|
1385
1435
|
}
|
data/ext/oj/parse.h
CHANGED
|
@@ -81,6 +81,7 @@ extern VALUE oj_num_as_value(NumInfo ni);
|
|
|
81
81
|
extern void oj_set_strict_callbacks(ParseInfo pi);
|
|
82
82
|
extern void oj_set_object_callbacks(ParseInfo pi);
|
|
83
83
|
extern void oj_set_compat_callbacks(ParseInfo pi);
|
|
84
|
+
extern void oj_compat_array_append_other(VALUE a, VALUE value);
|
|
84
85
|
extern void oj_set_custom_callbacks(ParseInfo pi);
|
|
85
86
|
extern void oj_set_wab_callbacks(ParseInfo pi);
|
|
86
87
|
|
|
@@ -108,6 +109,14 @@ static inline void parse_info_init(ParseInfo pi) {
|
|
|
108
109
|
|
|
109
110
|
extern void oj_scanner_init(void);
|
|
110
111
|
|
|
112
|
+
static inline void oj_compat_array_append(VALUE a, VALUE value) {
|
|
113
|
+
if (rb_cArray == RBASIC_CLASS(a)) {
|
|
114
|
+
rb_ary_push(a, value);
|
|
115
|
+
} else {
|
|
116
|
+
oj_compat_array_append_other(a, value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
111
120
|
static inline bool empty_ok(Options options) {
|
|
112
121
|
switch (options->mode) {
|
|
113
122
|
case ObjectMode:
|
data/ext/oj/parser.c
CHANGED
|
@@ -179,8 +179,8 @@ static const char neg_map[258] = "\
|
|
|
179
179
|
static const char zero_map[258] = "\
|
|
180
180
|
.........rs..r..................\
|
|
181
181
|
r...........u.t.................\
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
.....w.......................H..\
|
|
183
|
+
.....w.......................G..\
|
|
184
184
|
................................\
|
|
185
185
|
................................\
|
|
186
186
|
................................\
|
|
@@ -762,6 +762,12 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
762
762
|
calc_num(p);
|
|
763
763
|
// flow through
|
|
764
764
|
case CLOSE_OBJECT:
|
|
765
|
+
// Inside an object value_map is only in use right after a colon.
|
|
766
|
+
if (value_map == p->map && 0 < p->depth && OBJECT_FUN == p->stack[p->depth]) {
|
|
767
|
+
p->col = b - json - p->col + 1;
|
|
768
|
+
parse_error(p, "expected a value, not an object close");
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
765
771
|
p->map = (1 == p->depth) ? value_map : after_map;
|
|
766
772
|
if (p->depth <= 0 || OBJECT_FUN != p->stack[p->depth]) {
|
|
767
773
|
p->col = b - json - p->col + 1;
|
|
@@ -881,6 +887,11 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
881
887
|
p->num.shift++;
|
|
882
888
|
} else {
|
|
883
889
|
big_change(p);
|
|
890
|
+
if (0 == p->num.shift) {
|
|
891
|
+
// No fraction digit was read yet so big_change()
|
|
892
|
+
// did not add the '.' before the digits that follow.
|
|
893
|
+
buf_append(&p->buf, '.');
|
|
894
|
+
}
|
|
884
895
|
p->map = big_frac_map;
|
|
885
896
|
break;
|
|
886
897
|
}
|
|
@@ -893,6 +904,7 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
893
904
|
break;
|
|
894
905
|
case NUM_ZERO: p->map = zero_map; break;
|
|
895
906
|
case NEG_DIGIT:
|
|
907
|
+
p->map = digit_map;
|
|
896
908
|
for (; NUM_DIGIT == digit_map[*b]; b++) {
|
|
897
909
|
uint64_t x = p->num.fixnum * 10 + (uint64_t)(*b - '0');
|
|
898
910
|
|
|
@@ -905,7 +917,6 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
905
917
|
}
|
|
906
918
|
}
|
|
907
919
|
b--;
|
|
908
|
-
p->map = digit_map;
|
|
909
920
|
break;
|
|
910
921
|
case EXP_SIGN:
|
|
911
922
|
p->num.exp_neg = ('-' == *b);
|
|
@@ -964,10 +975,12 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
964
975
|
case NUM_SPC:
|
|
965
976
|
p->cur = b - json;
|
|
966
977
|
calc_num(p);
|
|
978
|
+
p->map = (0 == p->depth) ? value_map : after_map;
|
|
967
979
|
break;
|
|
968
980
|
case NUM_NEWLINE:
|
|
969
981
|
p->cur = b - json;
|
|
970
982
|
calc_num(p);
|
|
983
|
+
p->map = (0 == p->depth) ? value_map : after_map;
|
|
971
984
|
b++;
|
|
972
985
|
#ifdef SPACE_JUMP
|
|
973
986
|
// for (uint32_t *sj = (uint32_t*)b; 0x20202020 == *sj; sj++) { b += 4; }
|
|
@@ -1010,15 +1023,26 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
1010
1023
|
p->ri++;
|
|
1011
1024
|
p->ucode = p->ucode << 4 | (uint32_t)hex_map[*b];
|
|
1012
1025
|
if (4 <= p->ri) {
|
|
1013
|
-
byte
|
|
1014
|
-
|
|
1026
|
+
byte utf8[8];
|
|
1027
|
+
Buf buf = (':' == p->next_map[256]) ? &p->key : &p->buf;
|
|
1028
|
+
uint32_t code = p->ucode;
|
|
1015
1029
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1030
|
+
// A low surrogate right after a high one, which the previous
|
|
1031
|
+
// escape appended on its own, completes a pair.
|
|
1032
|
+
if (0xDC00 <= code && code <= 0xDFFF && 3 <= buf_len(buf)) {
|
|
1033
|
+
const byte *hi = (const byte *)buf->tail - 3;
|
|
1034
|
+
|
|
1035
|
+
if (0xED == hi[0] && 0xA0 <= hi[1] && hi[1] <= 0xAF) {
|
|
1036
|
+
uint32_t c1 = ((uint32_t)(hi[1] & 0x0F) << 6) | (hi[2] & 0x3F);
|
|
1037
|
+
|
|
1038
|
+
code = ((c1 << 10) | (code - 0xDC00)) + 0x00010000;
|
|
1039
|
+
buf->tail -= 3;
|
|
1021
1040
|
}
|
|
1041
|
+
}
|
|
1042
|
+
size_t ulen = unicodeToUtf8(code, utf8);
|
|
1043
|
+
|
|
1044
|
+
if (0 < ulen) {
|
|
1045
|
+
buf_append_string(buf, (const char *)utf8, ulen);
|
|
1022
1046
|
} else {
|
|
1023
1047
|
parse_error(p, "invalid unicode");
|
|
1024
1048
|
return;
|
|
@@ -1208,11 +1232,9 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
1208
1232
|
case '0':
|
|
1209
1233
|
case 'd':
|
|
1210
1234
|
case 'f':
|
|
1211
|
-
case 'z':
|
|
1212
1235
|
case 'X':
|
|
1213
1236
|
case 'D':
|
|
1214
1237
|
case 'g':
|
|
1215
|
-
case 'B':
|
|
1216
1238
|
case 'Y':
|
|
1217
1239
|
p->cur = b - json;
|
|
1218
1240
|
calc_num(p);
|
|
@@ -1462,6 +1484,23 @@ static void validate_primitives_are_complete(ojParser p) {
|
|
|
1462
1484
|
}
|
|
1463
1485
|
}
|
|
1464
1486
|
|
|
1487
|
+
// A top level number that stops after a '-', a '.', an 'e' or an exponent
|
|
1488
|
+
// sign is still waiting for a digit.
|
|
1489
|
+
static void validate_number_is_complete(ojParser p) {
|
|
1490
|
+
if (0 < p->depth) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
switch (p->map[256]) {
|
|
1494
|
+
case '-':
|
|
1495
|
+
case '.':
|
|
1496
|
+
case 'x':
|
|
1497
|
+
case 'z':
|
|
1498
|
+
case 'o':
|
|
1499
|
+
case 'B':
|
|
1500
|
+
case 'Z': parse_error(p, "incomplete number"); break;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1465
1504
|
static void validate_non_primitives_are_complete(ojParser p) {
|
|
1466
1505
|
if (0 >= p->depth) {
|
|
1467
1506
|
return;
|
|
@@ -1475,6 +1514,7 @@ static void validate_non_primitives_are_complete(ojParser p) {
|
|
|
1475
1514
|
|
|
1476
1515
|
static void validate_document_end(ojParser p) {
|
|
1477
1516
|
validate_primitives_are_complete(p);
|
|
1517
|
+
validate_number_is_complete(p);
|
|
1478
1518
|
validate_non_primitives_are_complete(p);
|
|
1479
1519
|
}
|
|
1480
1520
|
|
|
@@ -1487,19 +1527,20 @@ static void validate_document_end(ojParser p) {
|
|
|
1487
1527
|
*/
|
|
1488
1528
|
static VALUE parser_parse(VALUE self, VALUE json) {
|
|
1489
1529
|
ojParser p;
|
|
1490
|
-
int frozen = OBJ_FROZEN(json);
|
|
1491
1530
|
const byte *ptr;
|
|
1492
1531
|
|
|
1493
|
-
|
|
1532
|
+
StringValue(json);
|
|
1533
|
+
if (!OBJ_FROZEN(json)) {
|
|
1494
1534
|
rb_str_freeze(json);
|
|
1495
1535
|
}
|
|
1496
|
-
ptr = (const byte *)
|
|
1536
|
+
ptr = (const byte *)RSTRING_PTR(json);
|
|
1497
1537
|
|
|
1498
1538
|
TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
|
|
1499
1539
|
|
|
1500
1540
|
parser_reset(p);
|
|
1501
1541
|
p->start(p);
|
|
1502
1542
|
parse(p, ptr, (size_t)RSTRING_LEN(json), false);
|
|
1543
|
+
RB_GC_GUARD(json);
|
|
1503
1544
|
validate_document_end(p);
|
|
1504
1545
|
|
|
1505
1546
|
return p->result(p);
|
|
@@ -1599,7 +1640,7 @@ static VALUE parser_file(VALUE self, VALUE filename) {
|
|
|
1599
1640
|
|
|
1600
1641
|
TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
|
|
1601
1642
|
|
|
1602
|
-
path =
|
|
1643
|
+
path = StringValueCStr(filename);
|
|
1603
1644
|
|
|
1604
1645
|
parser_reset(p);
|
|
1605
1646
|
p->start(p);
|
data/ext/oj/rails.c
CHANGED
|
@@ -1062,6 +1062,7 @@ static VALUE encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *a
|
|
|
1062
1062
|
}
|
|
1063
1063
|
if (Yes == copts.circular) {
|
|
1064
1064
|
oj_cache8_delete(out.circ_cache);
|
|
1065
|
+
out.circ_cache = NULL;
|
|
1065
1066
|
}
|
|
1066
1067
|
|
|
1067
1068
|
oj_out_free(&out);
|
|
@@ -1316,12 +1317,15 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
1316
1317
|
size_t cnt = 0;
|
|
1317
1318
|
|
|
1318
1319
|
if (0.0 == d) {
|
|
1319
|
-
b
|
|
1320
|
+
b = buf;
|
|
1321
|
+
if (signbit(d)) {
|
|
1322
|
+
*b++ = '-';
|
|
1323
|
+
}
|
|
1320
1324
|
*b++ = '0';
|
|
1321
1325
|
*b++ = '.';
|
|
1322
1326
|
*b++ = '0';
|
|
1323
|
-
*b
|
|
1324
|
-
cnt =
|
|
1327
|
+
*b = '\0';
|
|
1328
|
+
cnt = b - buf;
|
|
1325
1329
|
} else {
|
|
1326
1330
|
if (isnan(d) || OJ_INFINITY == d || -OJ_INFINITY == d) {
|
|
1327
1331
|
strcpy(buf, "null");
|
|
@@ -1372,6 +1376,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
|
|
1372
1376
|
cnt--;
|
|
1373
1377
|
for (i = 0; i <= cnt; i++) {
|
|
1374
1378
|
if (out->opts->dump_opts.use) {
|
|
1379
|
+
assure_size(out, size);
|
|
1375
1380
|
if (0 < out->opts->dump_opts.array_size) {
|
|
1376
1381
|
APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
|
|
1377
1382
|
}
|
data/ext/oj/reader.c
CHANGED
|
@@ -45,6 +45,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
45
45
|
reader->line = 1;
|
|
46
46
|
reader->col = 0;
|
|
47
47
|
reader->free_head = 0;
|
|
48
|
+
reader->in_val = Qnil;
|
|
48
49
|
|
|
49
50
|
if (0 != fd) {
|
|
50
51
|
reader->read_func = read_from_fd;
|
|
@@ -52,6 +53,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
52
53
|
} else if (rb_cString == io_class) {
|
|
53
54
|
reader->read_func = 0;
|
|
54
55
|
reader->in_str = StringValuePtr(io);
|
|
56
|
+
reader->in_val = io;
|
|
55
57
|
reader->head = (char *)reader->in_str;
|
|
56
58
|
reader->tail = reader->head;
|
|
57
59
|
reader->read_end = reader->head + RSTRING_LEN(io);
|
|
@@ -60,6 +62,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
60
62
|
|
|
61
63
|
reader->read_func = 0;
|
|
62
64
|
reader->in_str = StringValuePtr(s);
|
|
65
|
+
reader->in_val = s;
|
|
63
66
|
reader->head = (char *)reader->in_str;
|
|
64
67
|
reader->tail = reader->head;
|
|
65
68
|
reader->read_end = reader->head + RSTRING_LEN(s);
|
|
@@ -79,6 +82,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
79
82
|
|
|
80
83
|
reader->read_func = 0;
|
|
81
84
|
reader->in_str = StringValuePtr(rstr);
|
|
85
|
+
reader->in_val = rstr;
|
|
82
86
|
reader->head = (char *)reader->in_str;
|
|
83
87
|
reader->tail = reader->head;
|
|
84
88
|
reader->read_end = reader->head + RSTRING_LEN(rstr);
|
data/ext/oj/reader.h
CHANGED
data/ext/oj/resolve.c
CHANGED
|
@@ -82,8 +82,9 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
|
|
|
82
82
|
|
|
83
83
|
VALUE
|
|
84
84
|
oj_name2struct(ParseInfo pi, VALUE nameVal, VALUE error_class) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
if (T_STRING != rb_type(nameVal)) {
|
|
86
|
+
oj_set_error_at(pi, error_class, __FILE__, __LINE__, "Invalid struct data");
|
|
87
|
+
return Qundef;
|
|
88
|
+
}
|
|
89
|
+
return resolve_classpath(pi, RSTRING_PTR(nameVal), RSTRING_LEN(nameVal), 0, error_class);
|
|
89
90
|
}
|