oj 3.7.4 → 3.11.2

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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -4
  3. data/ext/oj/buf.h +6 -34
  4. data/ext/oj/cache8.c +3 -3
  5. data/ext/oj/cache8.h +5 -33
  6. data/ext/oj/circarray.c +5 -9
  7. data/ext/oj/circarray.h +5 -8
  8. data/ext/oj/code.c +3 -6
  9. data/ext/oj/code.h +7 -10
  10. data/ext/oj/compat.c +11 -14
  11. data/ext/oj/custom.c +108 -75
  12. data/ext/oj/dump.c +132 -92
  13. data/ext/oj/dump.h +6 -7
  14. data/ext/oj/dump_compat.c +37 -34
  15. data/ext/oj/dump_leaf.c +3 -6
  16. data/ext/oj/dump_object.c +23 -17
  17. data/ext/oj/dump_strict.c +7 -9
  18. data/ext/oj/encode.h +6 -32
  19. data/ext/oj/err.c +2 -5
  20. data/ext/oj/err.h +6 -34
  21. data/ext/oj/extconf.rb +6 -0
  22. data/ext/oj/fast.c +39 -56
  23. data/ext/oj/hash.c +11 -39
  24. data/ext/oj/hash.h +5 -33
  25. data/ext/oj/hash_test.c +3 -31
  26. data/ext/oj/mimic_json.c +65 -44
  27. data/ext/oj/object.c +38 -69
  28. data/ext/oj/odd.c +18 -17
  29. data/ext/oj/odd.h +6 -9
  30. data/ext/oj/oj.c +139 -93
  31. data/ext/oj/oj.h +43 -35
  32. data/ext/oj/parse.c +164 -60
  33. data/ext/oj/parse.h +30 -31
  34. data/ext/oj/rails.c +119 -83
  35. data/ext/oj/rails.h +4 -7
  36. data/ext/oj/reader.c +5 -8
  37. data/ext/oj/reader.h +7 -10
  38. data/ext/oj/resolve.c +4 -7
  39. data/ext/oj/resolve.h +4 -7
  40. data/ext/oj/rxclass.c +8 -11
  41. data/ext/oj/rxclass.h +8 -11
  42. data/ext/oj/saj.c +9 -12
  43. data/ext/oj/scp.c +4 -7
  44. data/ext/oj/sparse.c +67 -33
  45. data/ext/oj/stream_writer.c +16 -15
  46. data/ext/oj/strict.c +9 -12
  47. data/ext/oj/string_writer.c +27 -8
  48. data/ext/oj/trace.c +5 -8
  49. data/ext/oj/trace.h +9 -12
  50. data/ext/oj/util.c +136 -0
  51. data/ext/oj/util.h +19 -0
  52. data/ext/oj/val_stack.c +28 -36
  53. data/ext/oj/val_stack.h +19 -50
  54. data/ext/oj/wab.c +29 -29
  55. data/lib/oj.rb +0 -8
  56. data/lib/oj/json.rb +1 -1
  57. data/lib/oj/mimic.rb +46 -2
  58. data/lib/oj/version.rb +2 -2
  59. data/pages/Modes.md +47 -45
  60. data/pages/Options.md +43 -10
  61. data/pages/Rails.md +60 -21
  62. data/pages/Security.md +1 -1
  63. data/test/activesupport5/abstract_unit.rb +45 -0
  64. data/test/activesupport5/decoding_test.rb +68 -60
  65. data/test/activesupport5/encoding_test.rb +111 -96
  66. data/test/activesupport5/encoding_test_cases.rb +33 -25
  67. data/test/activesupport5/test_helper.rb +43 -21
  68. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  69. data/test/activesupport6/abstract_unit.rb +44 -0
  70. data/test/activesupport6/decoding_test.rb +133 -0
  71. data/test/activesupport6/encoding_test.rb +507 -0
  72. data/test/activesupport6/encoding_test_cases.rb +98 -0
  73. data/test/activesupport6/test_common.rb +17 -0
  74. data/test/activesupport6/test_helper.rb +163 -0
  75. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  76. data/test/bar.rb +24 -6
  77. data/test/baz.rb +16 -0
  78. data/test/foo.rb +26 -57
  79. data/test/helper.rb +10 -0
  80. data/test/json_gem/json_common_interface_test.rb +8 -3
  81. data/test/json_gem/json_generator_test.rb +15 -3
  82. data/test/json_gem/test_helper.rb +8 -0
  83. data/test/prec.rb +23 -0
  84. data/test/sample_json.rb +1 -1
  85. data/test/test_compat.rb +21 -10
  86. data/test/test_custom.rb +135 -8
  87. data/test/test_integer_range.rb +1 -2
  88. data/test/test_object.rb +35 -2
  89. data/test/test_rails.rb +35 -0
  90. data/test/test_strict.rb +24 -1
  91. data/test/test_various.rb +52 -63
  92. data/test/test_writer.rb +19 -2
  93. data/test/tests.rb +1 -0
  94. data/test/zoo.rb +13 -0
  95. metadata +100 -75
data/ext/oj/oj.h CHANGED
@@ -1,12 +1,9 @@
1
- /* oj.h
2
- * Copyright (c) 2011, Peter Ohler
3
- * All rights reserved.
4
- */
1
+ // Copyright (c) 2011 Peter Ohler. All rights reserved.
5
2
 
6
- #ifndef __OJ_H__
7
- #define __OJ_H__
3
+ #ifndef OJ_H
4
+ #define OJ_H
8
5
 
9
- #if defined(__cplusplus)
6
+ #if defined(cplusplus)
10
7
  extern "C" {
11
8
  #if 0
12
9
  } /* satisfy cc-mode */
@@ -21,7 +18,7 @@ extern "C" {
21
18
  #include <stdint.h>
22
19
  #include <stdbool.h>
23
20
 
24
- #if HAVE_LIBPTHREAD
21
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
25
22
  #include <pthread.h>
26
23
  #endif
27
24
  #include "cache8.h"
@@ -69,14 +66,17 @@ typedef enum {
69
66
  JSONEsc = 'j',
70
67
  XSSEsc = 'x',
71
68
  ASCIIEsc = 'a',
72
- JXEsc = 'r', // json
69
+ JXEsc = 'g', // json gem
70
+ RailsXEsc = 'r', // rails xss mode
73
71
  RailsEsc = 'R', // rails non escape
74
72
  } Encoding;
75
73
 
76
74
  typedef enum {
77
75
  BigDec = 'b',
78
76
  FloatDec = 'f',
79
- AutoDec = 'a'
77
+ AutoDec = 'a',
78
+ FastDec = 'F',
79
+ RubyDec = 'r',
80
80
  } BigLoad;
81
81
 
82
82
  typedef enum {
@@ -107,7 +107,7 @@ typedef enum {
107
107
  // Add the fast versions if necessary. Maybe unparse as well if needed.
108
108
  } DumpCaller;
109
109
 
110
- typedef struct _DumpOpts {
110
+ typedef struct _dumpOpts {
111
111
  bool use;
112
112
  char indent_str[16];
113
113
  char before_sep[16];
@@ -124,7 +124,7 @@ typedef struct _DumpOpts {
124
124
  int max_depth;
125
125
  } *DumpOpts;
126
126
 
127
- typedef struct _Options {
127
+ typedef struct _options {
128
128
  int indent; // indention for dump, default 2
129
129
  char circular; // YesNo
130
130
  char auto_define; // YesNo
@@ -135,9 +135,11 @@ typedef struct _Options {
135
135
  char time_format; // TimeFormat
136
136
  char bigdec_as_num; // YesNo
137
137
  char bigdec_load; // BigLoad
138
+ char compat_bigdec; // boolean (0 or 1)
138
139
  char to_hash; // YesNo
139
140
  char to_json; // YesNo
140
141
  char as_json; // YesNo
142
+ char raw_json; // YesNo
141
143
  char nilnil; // YesNo
142
144
  char empty_string; // YesNo
143
145
  char allow_gc; // allow GC during parse
@@ -146,8 +148,11 @@ typedef struct _Options {
146
148
  char create_ok; // YesNo allow create_id
147
149
  char allow_nan; // YEsyNo for parsing only
148
150
  char trace; // YesNo
149
- int64_t integer_range_min; // dump numbers outside range as string
150
- int64_t integer_range_max;
151
+ char safe; // YesNo
152
+ char sec_prec_set; // boolean (0 or 1)
153
+ char ignore_under; // YesNo - ignore attrs starting with _ if true in object and custom modes
154
+ int64_t int_range_min; // dump numbers below as string
155
+ int64_t int_range_max; // dump numbers above as string
151
156
  const char *create_id; // 0 or string
152
157
  size_t create_id_len; // length of create_id
153
158
  int sec_prec; // second precision when dumping time
@@ -155,28 +160,28 @@ typedef struct _Options {
155
160
  char float_fmt[7]; // float format for dumping, if empty use Ruby
156
161
  VALUE hash_class; // class to use in place of Hash on load
157
162
  VALUE array_class; // class to use in place of Array on load
158
- struct _DumpOpts dump_opts;
159
- struct _RxClass str_rx;
163
+ struct _dumpOpts dump_opts;
164
+ struct _rxClass str_rx;
160
165
  VALUE *ignore; // Qnil terminated array of classes or NULL
161
166
  } *Options;
162
167
 
163
- struct _Out;
164
- typedef void (*DumpFunc)(VALUE obj, int depth, struct _Out *out, bool as_ok);
168
+ struct _out;
169
+ typedef void (*DumpFunc)(VALUE obj, int depth, struct _out *out, bool as_ok);
165
170
 
166
171
  // rails optimize
167
- typedef struct _ROpt {
172
+ typedef struct _rOpt {
168
173
  VALUE clas;
169
174
  bool on;
170
175
  DumpFunc dump;
171
176
  } *ROpt;
172
177
 
173
- typedef struct _ROptTable {
178
+ typedef struct _rOptTable {
174
179
  int len;
175
180
  int alen;
176
181
  ROpt table;
177
182
  } *ROptTable;
178
183
 
179
- typedef struct _Out {
184
+ typedef struct _out {
180
185
  char *buf;
181
186
  char *end;
182
187
  char *cur;
@@ -194,18 +199,18 @@ typedef struct _Out {
194
199
  ROptTable ropts;
195
200
  } *Out;
196
201
 
197
- typedef struct _StrWriter {
198
- struct _Out out;
199
- struct _Options opts;
202
+ typedef struct _strWriter {
203
+ struct _out out;
204
+ struct _options opts;
200
205
  int depth;
201
206
  char *types; // DumpType
202
207
  char *types_end;
203
208
  int keyWritten;
204
-
209
+
205
210
  } *StrWriter;
206
211
 
207
- typedef struct _StreamWriter {
208
- struct _StrWriter sw;
212
+ typedef struct _streamWriter {
213
+ struct _strWriter sw;
209
214
  StreamWriterType type;
210
215
  VALUE stream;
211
216
  int fd;
@@ -218,16 +223,16 @@ enum {
218
223
  COL_VAL = 0x02,
219
224
  RUBY_VAL = 0x03
220
225
  };
221
-
222
- typedef struct _Leaf {
223
- struct _Leaf *next;
226
+
227
+ typedef struct _leaf {
228
+ struct _leaf *next;
224
229
  union {
225
230
  const char *key; // hash key
226
231
  size_t index; // array index, 0 is not set
227
232
  };
228
233
  union {
229
234
  char *str; // pointer to location in json string or allocated
230
- struct _Leaf *elements; // array and hash elements
235
+ struct _leaf *elements; // array and hash elements
231
236
  VALUE value;
232
237
  };
233
238
  uint8_t rtype;
@@ -284,7 +289,7 @@ extern void oj_parse_opt_match_string(RxClass rc, VALUE ropts);
284
289
  extern VALUE oj_rails_encode(int argc, VALUE *argv, VALUE self);
285
290
 
286
291
  extern VALUE Oj;
287
- extern struct _Options oj_default_options;
292
+ extern struct _options oj_default_options;
288
293
  extern rb_encoding *oj_utf8_encoding;
289
294
 
290
295
  extern VALUE oj_bag_class;
@@ -306,6 +311,7 @@ extern VALUE oj_array_class_sym;
306
311
  extern VALUE oj_array_nl_sym;
307
312
  extern VALUE oj_ascii_only_sym;
308
313
  extern VALUE oj_create_additions_sym;
314
+ extern VALUE oj_decimal_class_sym;
309
315
  extern VALUE oj_hash_class_sym;
310
316
  extern VALUE oj_indent_sym;
311
317
  extern VALUE oj_max_nesting_sym;
@@ -348,6 +354,7 @@ extern ID oj_readpartial_id;
348
354
  extern ID oj_replace_id;
349
355
  extern ID oj_stat_id;
350
356
  extern ID oj_string_id;
357
+ extern ID oj_raw_json_id;
351
358
  extern ID oj_to_h_id;
352
359
  extern ID oj_to_hash_id;
353
360
  extern ID oj_to_json_id;
@@ -364,17 +371,18 @@ extern ID oj_write_id;
364
371
 
365
372
  extern bool oj_use_hash_alt;
366
373
  extern bool oj_use_array_alt;
374
+ extern bool string_writer_optimized;
367
375
 
368
- #if HAVE_LIBPTHREAD
376
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
369
377
  extern pthread_mutex_t oj_cache_mutex;
370
378
  #else
371
379
  extern VALUE oj_cache_mutex;
372
380
  #endif
373
381
 
374
- #if defined(__cplusplus)
382
+ #if defined(cplusplus)
375
383
  #if 0
376
384
  { /* satisfy cc-mode */
377
385
  #endif
378
386
  } /* extern "C" { */
379
387
  #endif
380
- #endif /* __OJ_H__ */
388
+ #endif /* OJ_H */
data/ext/oj/parse.c CHANGED
@@ -1,13 +1,11 @@
1
- /* parse.c
2
- * Copyright (c) 2013, Peter Ohler
3
- * All rights reserved.
4
- */
1
+ // Copyright (c) 2013 Peter Ohler. All rights reserved.
5
2
 
6
3
  #include <stdlib.h>
7
4
  #include <stdio.h>
8
5
  #include <string.h>
9
6
  #include <unistd.h>
10
7
  #include <math.h>
8
+ #include <ruby/util.h>
11
9
 
12
10
  #include "oj.h"
13
11
  #include "encode.h"
@@ -188,7 +186,7 @@ unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
188
186
  // entered at /
189
187
  static void
190
188
  read_escaped_str(ParseInfo pi, const char *start) {
191
- struct _Buf buf;
189
+ struct _buf buf;
192
190
  const char *s;
193
191
  int cnt = (int)(pi->cur - start);
194
192
  uint32_t code;
@@ -214,18 +212,6 @@ read_escaped_str(ParseInfo pi, const char *start) {
214
212
  case '"': buf_append(&buf, '"'); break;
215
213
  case '/': buf_append(&buf, '/'); break;
216
214
  case '\\': buf_append(&buf, '\\'); break;
217
- case '\'':
218
- // The json gem claims this is not an error despite the
219
- // ECMA-404 indicating it is not valid.
220
- if (CompatMode == pi->options.mode) {
221
- buf_append(&buf, '\'');
222
- } else {
223
- pi->cur = s;
224
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
225
- buf_cleanup(&buf);
226
- return;
227
- }
228
- break;
229
215
  case 'u':
230
216
  s++;
231
217
  if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
@@ -265,6 +251,12 @@ read_escaped_str(ParseInfo pi, const char *start) {
265
251
  }
266
252
  break;
267
253
  default:
254
+ // The json gem claims this is not an error despite the
255
+ // ECMA-404 indicating it is not valid.
256
+ if (CompatMode == pi->options.mode) {
257
+ buf_append(&buf, *s);
258
+ break;
259
+ }
268
260
  pi->cur = s;
269
261
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
270
262
  buf_cleanup(&buf);
@@ -378,7 +370,7 @@ read_str(ParseInfo pi) {
378
370
 
379
371
  static void
380
372
  read_num(ParseInfo pi) {
381
- struct _NumInfo ni;
373
+ struct _numInfo ni;
382
374
  Val parent = stack_peek(&pi->stack);
383
375
 
384
376
  ni.str = pi->cur;
@@ -392,13 +384,23 @@ read_num(ParseInfo pi) {
392
384
  ni.infinity = 0;
393
385
  ni.nan = 0;
394
386
  ni.neg = 0;
395
- ni.hasExp = 0;
396
- ni.no_big = (FloatDec == pi->options.bigdec_load);
387
+ ni.has_exp = 0;
388
+ if (CompatMode == pi->options.mode) {
389
+ ni.no_big = !pi->options.compat_bigdec;
390
+ ni.bigdec_load = pi->options.compat_bigdec;
391
+ } else {
392
+ ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
393
+ ni.bigdec_load = pi->options.bigdec_load;
394
+ }
397
395
 
398
396
  if ('-' == *pi->cur) {
399
397
  pi->cur++;
400
398
  ni.neg = 1;
401
399
  } else if ('+' == *pi->cur) {
400
+ if (StrictMode == pi->options.mode) {
401
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
402
+ return;
403
+ }
402
404
  pi->cur++;
403
405
  }
404
406
  if ('I' == *pi->cur) {
@@ -418,7 +420,7 @@ read_num(ParseInfo pi) {
418
420
  } else {
419
421
  int dec_cnt = 0;
420
422
  bool zero1 = false;
421
-
423
+
422
424
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
423
425
  if (0 == ni.i && '0' == *pi->cur) {
424
426
  zero1 = true;
@@ -445,8 +447,14 @@ read_num(ParseInfo pi) {
445
447
  if ('.' == *pi->cur) {
446
448
  pi->cur++;
447
449
  // A trailing . is not a valid decimal but if encountered allow it
448
- // except when mimicing the JSON gem.
449
- if (CompatMode == pi->options.mode) {
450
+ // except when mimicing the JSON gem or in strict mode.
451
+ if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
452
+ int pos = (int)(pi->cur - ni.str);
453
+
454
+ if (1 == pos || (2 == pos && ni.neg)) {
455
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
456
+ return;
457
+ }
450
458
  if (*pi->cur < '0' || '9' < *pi->cur) {
451
459
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
452
460
  return;
@@ -458,18 +466,26 @@ read_num(ParseInfo pi) {
458
466
  if (0 < ni.num || 0 < ni.i) {
459
467
  dec_cnt++;
460
468
  }
461
- ni.num = ni.num * 10 + d;
462
- ni.div *= 10;
463
- ni.di++;
464
- if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
465
- ni.big = 1;
469
+ if (INT64_MAX <= ni.div) {
470
+ if (!ni.no_big) {
471
+ ni.big = true;
472
+ }
473
+ } else {
474
+ ni.num = ni.num * 10 + d;
475
+ ni.div *= 10;
476
+ ni.di++;
477
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
478
+ if (!ni.no_big) {
479
+ ni.big = true;
480
+ }
481
+ }
466
482
  }
467
483
  }
468
484
  }
469
485
  if ('e' == *pi->cur || 'E' == *pi->cur) {
470
486
  int eneg = 0;
471
487
 
472
- ni.hasExp = 1;
488
+ ni.has_exp = 1;
473
489
  pi->cur++;
474
490
  if ('-' == *pi->cur) {
475
491
  pi->cur++;
@@ -480,7 +496,7 @@ read_num(ParseInfo pi) {
480
496
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
481
497
  ni.exp = ni.exp * 10 + (*pi->cur - '0');
482
498
  if (EXP_MAX <= ni.exp) {
483
- ni.big = 1;
499
+ ni.big = true;
484
500
  }
485
501
  }
486
502
  if (eneg) {
@@ -500,7 +516,11 @@ read_num(ParseInfo pi) {
500
516
  ni.nan = 1;
501
517
  }
502
518
  }
503
- if (BigDec == pi->options.bigdec_load) {
519
+ if (CompatMode == pi->options.mode) {
520
+ if (pi->options.compat_bigdec) {
521
+ ni.big = 1;
522
+ }
523
+ } else if (BigDec == pi->options.bigdec_load) {
504
524
  ni.big = 1;
505
525
  }
506
526
  if (0 == parent) {
@@ -607,7 +627,7 @@ oj_parse2(ParseInfo pi) {
607
627
  while (1) {
608
628
  if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
609
629
  VALUE err_clas = oj_get_json_err_class("NestingError");
610
-
630
+
611
631
  oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
612
632
  pi->err_class = err_clas;
613
633
  return;
@@ -729,6 +749,70 @@ oj_parse2(ParseInfo pi) {
729
749
  }
730
750
  }
731
751
 
752
+ static VALUE
753
+ rescue_big_decimal(VALUE str, VALUE ignore) {
754
+ rb_raise(oj_parse_error_class, "Invalid value for BigDecimal()");
755
+ return Qnil;
756
+ }
757
+
758
+ static VALUE
759
+ parse_big_decimal(VALUE str) {
760
+ return rb_funcall(rb_cObject, oj_bigdecimal_id, 1, str);
761
+ }
762
+
763
+ static long double exp_plus[] = {
764
+ 1.0,
765
+ 1.0e1,
766
+ 1.0e2,
767
+ 1.0e3,
768
+ 1.0e4,
769
+ 1.0e5,
770
+ 1.0e6,
771
+ 1.0e7,
772
+ 1.0e8,
773
+ 1.0e9,
774
+ 1.0e10,
775
+ 1.0e11,
776
+ 1.0e12,
777
+ 1.0e13,
778
+ 1.0e14,
779
+ 1.0e15,
780
+ 1.0e16,
781
+ 1.0e17,
782
+ 1.0e18,
783
+ 1.0e19,
784
+ 1.0e20,
785
+ 1.0e21,
786
+ 1.0e22,
787
+ 1.0e23,
788
+ 1.0e24,
789
+ 1.0e25,
790
+ 1.0e26,
791
+ 1.0e27,
792
+ 1.0e28,
793
+ 1.0e29,
794
+ 1.0e30,
795
+ 1.0e31,
796
+ 1.0e32,
797
+ 1.0e33,
798
+ 1.0e34,
799
+ 1.0e35,
800
+ 1.0e36,
801
+ 1.0e37,
802
+ 1.0e38,
803
+ 1.0e39,
804
+ 1.0e40,
805
+ 1.0e41,
806
+ 1.0e42,
807
+ 1.0e43,
808
+ 1.0e44,
809
+ 1.0e45,
810
+ 1.0e46,
811
+ 1.0e47,
812
+ 1.0e48,
813
+ 1.0e49,
814
+ };
815
+
732
816
  VALUE
733
817
  oj_num_as_value(NumInfo ni) {
734
818
  volatile VALUE rnum = Qnil;
@@ -741,7 +825,7 @@ oj_num_as_value(NumInfo ni) {
741
825
  }
742
826
  } else if (ni->nan) {
743
827
  rnum = rb_float_new(0.0/0.0);
744
- } else if (1 == ni->div && 0 == ni->exp) { // fixnum
828
+ } else if (1 == ni->div && 0 == ni->exp && !ni->has_exp) { // fixnum
745
829
  if (ni->big) {
746
830
  if (256 > ni->len) {
747
831
  char buf[256];
@@ -766,35 +850,45 @@ oj_num_as_value(NumInfo ni) {
766
850
  }
767
851
  } else { // decimal
768
852
  if (ni->big) {
769
- rnum = rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(ni->str, ni->len));
853
+ volatile VALUE bd = rb_str_new(ni->str, ni->len);
854
+
855
+ rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
770
856
  if (ni->no_big) {
771
857
  rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
772
858
  }
773
- } else {
774
- // All these machinations are to get rounding to work better.
775
- long double d = (long double)ni->i * (long double)ni->div + (long double)ni->num;
859
+ } else if (FastDec == ni->bigdec_load) {
860
+ long double ld = (long double)ni->i * (long double)ni->div + (long double)ni->num;
776
861
  int x = (int)((int64_t)ni->exp - ni->di);
777
862
 
778
- // Rounding sometimes cuts off the last digit even if there are only
779
- // 15 digits. This attempts to fix those few cases where this
780
- // occurs.
781
- if ((long double)INT64_MAX > d && (int64_t)d != (ni->i * ni->div + ni->num)) {
782
- rnum = rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(ni->str, ni->len));
783
- if (ni->no_big) {
784
- rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
785
- }
786
- } else {
787
- d = roundl(d);
788
- if (0 < x) {
789
- d *= powl(10.0L, x);
790
- } else if (0 > x) {
791
- d /= powl(10.0L, -x);
863
+ if (0 < x) {
864
+ if (x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
865
+ ld *= exp_plus[x];
866
+ } else {
867
+ ld *= powl(10.0, x);
792
868
  }
793
- if (ni->neg) {
794
- d = -d;
869
+ } else if (x < 0) {
870
+ if (-x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
871
+ ld /= exp_plus[-x];
872
+ } else {
873
+ ld /= powl(10.0, -x);
795
874
  }
796
- rnum = rb_float_new((double)d);
797
875
  }
876
+ if (ni->neg) {
877
+ ld = -ld;
878
+ }
879
+ rnum = rb_float_new((double)ld);
880
+ } else if (RubyDec == ni->bigdec_load) {
881
+ volatile VALUE sv = rb_str_new(ni->str, ni->len);
882
+
883
+ rnum = rb_funcall(sv, rb_intern("to_f"), 0);
884
+ } else {
885
+ char *end;
886
+ double d = strtod(ni->str, &end);
887
+
888
+ if ((long)ni->len != (long)(end - ni->str)) {
889
+ rb_raise(oj_parse_error_class, "Invalid float");
890
+ }
891
+ rnum = rb_float_new(d);
798
892
  }
799
893
  }
800
894
  return rnum;
@@ -816,6 +910,12 @@ oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const
816
910
  if (p + 3 < end) {
817
911
  *p++ = ' ';
818
912
  *p++ = '(';
913
+ *p++ = 'a';
914
+ *p++ = 'f';
915
+ *p++ = 't';
916
+ *p++ = 'e';
917
+ *p++ = 'r';
918
+ *p++ = ' ';
819
919
  start = p;
820
920
  for (vp = pi->stack.head; vp < pi->stack.tail; vp++) {
821
921
  if (end <= p + 1 + vp->klen) {
@@ -833,7 +933,7 @@ oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const
833
933
  break;
834
934
  }
835
935
  p += snprintf(p, end - p, "[%ld]", RARRAY_LEN(vp->val));
836
- }
936
+ }
837
937
  }
838
938
  }
839
939
  *p++ = ')';
@@ -976,12 +1076,12 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
976
1076
  if (0 != line) {
977
1077
  VALUE ec = rb_obj_class(rb_errinfo());
978
1078
 
979
- if (rb_eIOError != ec) {
980
- goto CLEANUP;
981
- }
982
1079
  if (rb_eArgError != ec && 0 != ec) {
983
1080
  err_class = ec;
984
1081
  }
1082
+ if (rb_eIOError != ec) {
1083
+ goto CLEANUP;
1084
+ }
985
1085
  }
986
1086
  if (NULL != (v = stack_peek(&pi->stack))) {
987
1087
  switch (v->next) {
@@ -1021,7 +1121,7 @@ CLEANUP:
1021
1121
  if (Qnil != pi->err_class) {
1022
1122
  pi->err.clas = pi->err_class;
1023
1123
  }
1024
- if (CompatMode == pi->options.mode) {
1124
+ if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
1025
1125
  // The json gem requires the error message be UTF-8 encoded. In
1026
1126
  // additional the complete JSON source must be returned. There
1027
1127
  // does not seem to be a size limit.
@@ -1033,6 +1133,10 @@ CLEANUP:
1033
1133
  msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
1034
1134
  }
1035
1135
  args[0] = msg;
1136
+ if (pi->err.clas == oj_parse_error_class) {
1137
+ // The error was an Oj::ParseError so change to a JSON::ParserError.
1138
+ pi->err.clas = oj_json_parser_error_class;
1139
+ }
1036
1140
  rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
1037
1141
  } else {
1038
1142
  oj_err_raise(&pi->err);
@@ -1050,7 +1154,7 @@ CLEANUP:
1050
1154
  case T_CLASS:
1051
1155
  case T_STRING:
1052
1156
  case T_SYMBOL: {
1053
- struct _Err err;
1157
+ struct _err err;
1054
1158
 
1055
1159
  if (Qnil == pi->err_class) {
1056
1160
  err.clas = oj_parse_error_class;