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.
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
- oj_dump_obj_to_json_using_params(*argv, &copts, &out, 1, active_hack);
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
- oj_dump_obj_to_json_using_params(*argv, copts, &out, argc - 1, argv + 1);
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
- oj_dump_obj_to_json_using_params(*argv, copts, &out, 1, active_hack);
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
- oj_dump_obj_to_json_using_params(self, &copts, &out, argc, argv);
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 = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
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 = StringValuePtr(*argv);
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
- oj_dump_obj_to_json_using_params(*argv, &copts, &out, argc - 1, argv + 1);
1820
+ return oj_dump_obj_to_str(*argv, &copts, &out, argc - 1, argv + 1);
1821
+ }
1822
1822
 
1823
- if (0 == out.buf) {
1824
- rb_raise(rb_eNoMemError, "Not enough memory.");
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
- rstr = rb_utf8_str_new_cstr(out.buf);
1853
+ }
1827
1854
 
1828
- oj_out_free(&out);
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
- return rstr;
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 copts = oj_default_options;
1898
+ struct _options copts;
1899
+ struct write_call_arg arg = {&copts, argc, argv};
1845
1900
 
1846
- if (3 == argc) {
1847
- oj_parse_options(argv[2], &copts);
1848
- }
1849
- oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
1850
- oj_free_call_options(&copts);
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 copts = oj_default_options;
1928
+ struct _options copts = oj_default_options;
1929
+ struct write_call_arg arg = {&copts, argc, argv};
1867
1930
 
1868
- if (3 == argc) {
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 Qnil;
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
- s = rb_funcall2(input, oj_string_id, 0, 0);
1212
- oj_pi_set_input_str(pi, &s);
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
- .............................H..\
183
- .............................G..\
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 utf8[8];
1014
- size_t ulen = unicodeToUtf8(p->ucode, utf8);
1026
+ byte utf8[8];
1027
+ Buf buf = (':' == p->next_map[256]) ? &p->key : &p->buf;
1028
+ uint32_t code = p->ucode;
1015
1029
 
1016
- if (0 < ulen) {
1017
- if (':' == p->next_map[256]) {
1018
- buf_append_string(&p->key, (const char *)utf8, ulen);
1019
- } else {
1020
- buf_append_string(&p->buf, (const char *)utf8, ulen);
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
- if (!frozen) {
1532
+ StringValue(json);
1533
+ if (!OBJ_FROZEN(json)) {
1494
1534
  rb_str_freeze(json);
1495
1535
  }
1496
- ptr = (const byte *)StringValuePtr(json);
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 = StringValuePtr(filename);
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 = buf;
1320
+ b = buf;
1321
+ if (signbit(d)) {
1322
+ *b++ = '-';
1323
+ }
1320
1324
  *b++ = '0';
1321
1325
  *b++ = '.';
1322
1326
  *b++ = '0';
1323
- *b++ = '\0';
1324
- cnt = 3;
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
@@ -24,6 +24,7 @@ typedef struct _reader {
24
24
  VALUE io;
25
25
  const char *in_str;
26
26
  };
27
+ VALUE in_val;
27
28
  } *Reader;
28
29
 
29
30
  extern void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s);
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
- size_t len = RSTRING_LEN(nameVal);
86
- const char *str = StringValuePtr(nameVal);
87
-
88
- return resolve_classpath(pi, str, len, 0, error_class);
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
  }