oj 3.7.4 → 3.11.2

Sign up to get free protection for your applications and to get access to all the features.
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;