oj 3.11.5 → 3.16.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1421 -0
  3. data/README.md +19 -5
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +20 -6
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +10 -9
  9. data/ext/oj/circarray.c +8 -6
  10. data/ext/oj/circarray.h +2 -2
  11. data/ext/oj/code.c +19 -33
  12. data/ext/oj/code.h +2 -2
  13. data/ext/oj/compat.c +27 -77
  14. data/ext/oj/custom.c +86 -179
  15. data/ext/oj/debug.c +126 -0
  16. data/ext/oj/dump.c +256 -249
  17. data/ext/oj/dump.h +26 -12
  18. data/ext/oj/dump_compat.c +565 -642
  19. data/ext/oj/dump_leaf.c +17 -63
  20. data/ext/oj/dump_object.c +65 -187
  21. data/ext/oj/dump_strict.c +27 -51
  22. data/ext/oj/encoder.c +43 -0
  23. data/ext/oj/err.c +2 -13
  24. data/ext/oj/err.h +24 -8
  25. data/ext/oj/extconf.rb +21 -6
  26. data/ext/oj/fast.c +149 -149
  27. data/ext/oj/intern.c +313 -0
  28. data/ext/oj/intern.h +22 -0
  29. data/ext/oj/mem.c +318 -0
  30. data/ext/oj/mem.h +53 -0
  31. data/ext/oj/mimic_json.c +121 -106
  32. data/ext/oj/object.c +85 -162
  33. data/ext/oj/odd.c +89 -67
  34. data/ext/oj/odd.h +15 -15
  35. data/ext/oj/oj.c +542 -411
  36. data/ext/oj/oj.h +99 -73
  37. data/ext/oj/parse.c +175 -187
  38. data/ext/oj/parse.h +26 -24
  39. data/ext/oj/parser.c +1600 -0
  40. data/ext/oj/parser.h +101 -0
  41. data/ext/oj/rails.c +112 -159
  42. data/ext/oj/rails.h +1 -1
  43. data/ext/oj/reader.c +11 -14
  44. data/ext/oj/reader.h +4 -2
  45. data/ext/oj/resolve.c +5 -24
  46. data/ext/oj/rxclass.c +7 -6
  47. data/ext/oj/rxclass.h +1 -1
  48. data/ext/oj/saj.c +22 -33
  49. data/ext/oj/saj2.c +584 -0
  50. data/ext/oj/saj2.h +23 -0
  51. data/ext/oj/scp.c +5 -28
  52. data/ext/oj/sparse.c +28 -72
  53. data/ext/oj/stream_writer.c +50 -40
  54. data/ext/oj/strict.c +56 -61
  55. data/ext/oj/string_writer.c +72 -39
  56. data/ext/oj/trace.h +31 -4
  57. data/ext/oj/usual.c +1218 -0
  58. data/ext/oj/usual.h +69 -0
  59. data/ext/oj/util.h +1 -1
  60. data/ext/oj/val_stack.c +14 -3
  61. data/ext/oj/val_stack.h +8 -7
  62. data/ext/oj/validate.c +46 -0
  63. data/ext/oj/wab.c +63 -88
  64. data/lib/oj/active_support_helper.rb +1 -3
  65. data/lib/oj/bag.rb +7 -1
  66. data/lib/oj/easy_hash.rb +4 -5
  67. data/lib/oj/error.rb +1 -2
  68. data/lib/oj/json.rb +162 -150
  69. data/lib/oj/mimic.rb +9 -7
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/schandler.rb +5 -4
  72. data/lib/oj/state.rb +12 -8
  73. data/lib/oj/version.rb +1 -2
  74. data/lib/oj.rb +2 -0
  75. data/pages/Compatibility.md +1 -1
  76. data/pages/InstallOptions.md +20 -0
  77. data/pages/JsonGem.md +15 -0
  78. data/pages/Modes.md +8 -3
  79. data/pages/Options.md +43 -5
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +14 -2
  82. data/test/_test_active.rb +8 -9
  83. data/test/_test_active_mimic.rb +7 -8
  84. data/test/_test_mimic_rails.rb +17 -20
  85. data/test/activerecord/result_test.rb +5 -6
  86. data/test/activesupport6/encoding_test.rb +63 -28
  87. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  88. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  89. data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
  90. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  91. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  92. data/test/files.rb +15 -15
  93. data/test/foo.rb +16 -45
  94. data/test/helper.rb +11 -8
  95. data/test/isolated/shared.rb +3 -2
  96. data/test/json_gem/json_addition_test.rb +2 -2
  97. data/test/json_gem/json_common_interface_test.rb +8 -6
  98. data/test/json_gem/json_encoding_test.rb +0 -0
  99. data/test/json_gem/json_ext_parser_test.rb +1 -0
  100. data/test/json_gem/json_fixtures_test.rb +3 -2
  101. data/test/json_gem/json_generator_test.rb +56 -38
  102. data/test/json_gem/json_generic_object_test.rb +11 -11
  103. data/test/json_gem/json_parser_test.rb +54 -47
  104. data/test/json_gem/json_string_matching_test.rb +9 -9
  105. data/test/json_gem/test_helper.rb +7 -3
  106. data/test/mem.rb +34 -0
  107. data/test/perf.rb +22 -27
  108. data/test/perf_compat.rb +31 -33
  109. data/test/perf_dump.rb +50 -0
  110. data/test/perf_fast.rb +80 -82
  111. data/test/perf_file.rb +27 -29
  112. data/test/perf_object.rb +65 -69
  113. data/test/perf_once.rb +59 -0
  114. data/test/perf_parser.rb +183 -0
  115. data/test/perf_saj.rb +46 -54
  116. data/test/perf_scp.rb +58 -69
  117. data/test/perf_simple.rb +41 -39
  118. data/test/perf_strict.rb +74 -82
  119. data/test/perf_wab.rb +67 -69
  120. data/test/prec.rb +5 -5
  121. data/test/sample/change.rb +0 -1
  122. data/test/sample/dir.rb +0 -1
  123. data/test/sample/doc.rb +0 -1
  124. data/test/sample/file.rb +0 -1
  125. data/test/sample/group.rb +0 -1
  126. data/test/sample/hasprops.rb +0 -1
  127. data/test/sample/layer.rb +0 -1
  128. data/test/sample/rect.rb +0 -1
  129. data/test/sample/shape.rb +0 -1
  130. data/test/sample/text.rb +0 -1
  131. data/test/sample.rb +16 -16
  132. data/test/sample_json.rb +8 -8
  133. data/test/test_compat.rb +95 -43
  134. data/test/test_custom.rb +73 -51
  135. data/test/test_debian.rb +7 -10
  136. data/test/test_fast.rb +135 -79
  137. data/test/test_file.rb +41 -30
  138. data/test/test_gc.rb +16 -5
  139. data/test/test_generate.rb +5 -5
  140. data/test/test_hash.rb +5 -5
  141. data/test/test_integer_range.rb +9 -9
  142. data/test/test_null.rb +20 -20
  143. data/test/test_object.rb +99 -96
  144. data/test/test_parser.rb +11 -0
  145. data/test/test_parser_debug.rb +27 -0
  146. data/test/test_parser_saj.rb +337 -0
  147. data/test/test_parser_usual.rb +251 -0
  148. data/test/test_rails.rb +2 -2
  149. data/test/test_saj.rb +10 -8
  150. data/test/test_scp.rb +37 -39
  151. data/test/test_strict.rb +40 -32
  152. data/test/test_various.rb +165 -84
  153. data/test/test_wab.rb +48 -44
  154. data/test/test_writer.rb +47 -47
  155. data/test/tests.rb +13 -5
  156. data/test/tests_mimic.rb +12 -3
  157. data/test/tests_mimic_addition.rb +12 -3
  158. metadata +74 -128
  159. data/ext/oj/hash.c +0 -131
  160. data/ext/oj/hash.h +0 -19
  161. data/ext/oj/hash_test.c +0 -491
  162. data/test/activesupport4/decoding_test.rb +0 -108
  163. data/test/activesupport4/encoding_test.rb +0 -531
  164. data/test/activesupport4/test_helper.rb +0 -41
  165. data/test/activesupport5/test_helper.rb +0 -72
  166. data/test/bar.rb +0 -35
  167. data/test/baz.rb +0 -16
  168. data/test/zoo.rb +0 -13
data/ext/oj/parse.c CHANGED
@@ -12,14 +12,19 @@
12
12
 
13
13
  #include "buf.h"
14
14
  #include "encode.h"
15
+ #include "mem.h"
15
16
  #include "oj.h"
16
17
  #include "rxclass.h"
17
18
  #include "val_stack.h"
18
19
 
20
+ #ifdef OJ_USE_SSE4_2
21
+ #include <nmmintrin.h>
22
+ #endif
23
+
19
24
  // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
20
25
  #define OJ_INFINITY (1.0 / 0.0)
21
26
 
22
- //#define EXP_MAX 1023
27
+ // #define EXP_MAX 1023
23
28
  #define EXP_MAX 100000
24
29
  #define DEC_MAX 15
25
30
 
@@ -44,11 +49,7 @@ static void skip_comment(ParseInfo pi) {
44
49
  pi->cur += 2;
45
50
  return;
46
51
  } else if (pi->end <= pi->cur) {
47
- oj_set_error_at(pi,
48
- oj_parse_error_class,
49
- __FILE__,
50
- __LINE__,
51
- "comment not terminated");
52
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
52
53
  return;
53
54
  }
54
55
  }
@@ -81,9 +82,8 @@ static void add_value(ParseInfo pi, VALUE rval) {
81
82
  break;
82
83
  case NEXT_HASH_VALUE:
83
84
  pi->hash_set_value(pi, parent, rval);
84
- if (0 != parent->key && 0 < parent->klen &&
85
- (parent->key < pi->json || pi->cur < parent->key)) {
86
- xfree((char *)parent->key);
85
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
86
+ OJ_R_FREE((char *)parent->key);
87
87
  parent->key = 0;
88
88
  }
89
89
  parent->next = NEXT_HASH_COMMA;
@@ -183,6 +183,46 @@ static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
183
183
  }
184
184
  }
185
185
 
186
+ static inline const char *scan_string_noSIMD(const char *str, const char *end) {
187
+ for (; '"' != *str; str++) {
188
+ if (end <= str || '\0' == *str || '\\' == *str) {
189
+ break;
190
+ }
191
+ }
192
+ return str;
193
+ }
194
+
195
+ #ifdef OJ_USE_SSE4_2
196
+ static inline const char *scan_string_SIMD(const char *str, const char *end) {
197
+ static const char chars[16] = "\x00\\\"";
198
+ const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
199
+ const char *_end = (const char *)(end - 16);
200
+
201
+ for (; str <= _end; str += 16) {
202
+ const __m128i string = _mm_loadu_si128((const __m128i *)str);
203
+ const int r = _mm_cmpestri(terminate,
204
+ 3,
205
+ string,
206
+ 16,
207
+ _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
208
+ if (r != 16) {
209
+ str = (char *)(str + r);
210
+ return str;
211
+ }
212
+ }
213
+
214
+ return scan_string_noSIMD(str, end);
215
+ }
216
+ #endif
217
+
218
+ static const char *(*scan_func)(const char *str, const char *end) = scan_string_noSIMD;
219
+
220
+ void oj_scanner_init(void) {
221
+ #ifdef OJ_USE_SSE4_2
222
+ scan_func = scan_string_SIMD;
223
+ #endif
224
+ }
225
+
186
226
  // entered at /
187
227
  static void read_escaped_str(ParseInfo pi, const char *start) {
188
228
  struct _buf buf;
@@ -192,19 +232,20 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
192
232
  Val parent = stack_peek(&pi->stack);
193
233
 
194
234
  buf_init(&buf);
195
- if (0 < cnt) {
196
- buf_append_string(&buf, start, cnt);
197
- }
198
- for (s = pi->cur; '"' != *s; s++) {
199
- if (s >= pi->end) {
200
- oj_set_error_at(pi,
201
- oj_parse_error_class,
202
- __FILE__,
203
- __LINE__,
204
- "quoted string not terminated");
235
+ buf_append_string(&buf, start, cnt);
236
+
237
+ for (s = pi->cur; '"' != *s;) {
238
+ const char *scanned = scan_func(s, pi->end);
239
+ if (scanned >= pi->end || '\0' == *scanned) {
240
+ // if (scanned >= pi->end) {
241
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
205
242
  buf_cleanup(&buf);
206
243
  return;
207
- } else if ('\\' == *s) {
244
+ }
245
+ buf_append_string(&buf, s, (size_t)(scanned - s));
246
+ s = scanned;
247
+
248
+ if ('\\' == *s) {
208
249
  s++;
209
250
  switch (*s) {
210
251
  case 'n': buf_append(&buf, '\n'); break;
@@ -234,11 +275,7 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
234
275
  break;
235
276
  }
236
277
  pi->cur = s;
237
- oj_set_error_at(pi,
238
- oj_parse_error_class,
239
- __FILE__,
240
- __LINE__,
241
- "invalid escaped character");
278
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
242
279
  buf_cleanup(&buf);
243
280
  return;
244
281
  }
@@ -265,16 +302,11 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
265
302
  break;
266
303
  }
267
304
  pi->cur = s;
268
- oj_set_error_at(pi,
269
- oj_parse_error_class,
270
- __FILE__,
271
- __LINE__,
272
- "invalid escaped character");
305
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
273
306
  buf_cleanup(&buf);
274
307
  return;
275
308
  }
276
- } else {
277
- buf_append(&buf, *s);
309
+ s++;
278
310
  }
279
311
  }
280
312
  if (0 == parent) {
@@ -290,7 +322,7 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
290
322
  case NEXT_HASH_KEY:
291
323
  if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
292
324
  parent->klen = buf_len(&buf);
293
- parent->key = malloc(parent->klen + 1);
325
+ parent->key = OJ_MALLOC(parent->klen + 1);
294
326
  memcpy((char *)parent->key, buf.head, parent->klen);
295
327
  *(char *)(parent->key + parent->klen) = '\0';
296
328
  } else {
@@ -302,9 +334,8 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
302
334
  break;
303
335
  case NEXT_HASH_VALUE:
304
336
  pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), start);
305
- if (0 != parent->key && 0 < parent->klen &&
306
- (parent->key < pi->json || pi->cur < parent->key)) {
307
- xfree((char *)parent->key);
337
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
338
+ OJ_R_FREE((char *)parent->key);
308
339
  parent->key = 0;
309
340
  }
310
341
  parent->next = NEXT_HASH_COMMA;
@@ -331,22 +362,20 @@ static void read_str(ParseInfo pi) {
331
362
  const char *str = pi->cur;
332
363
  Val parent = stack_peek(&pi->stack);
333
364
 
334
- for (; '"' != *pi->cur; pi->cur++) {
335
- if (pi->end <= pi->cur) {
336
- oj_set_error_at(pi,
337
- oj_parse_error_class,
338
- __FILE__,
339
- __LINE__,
340
- "quoted string not terminated");
341
- return;
342
- } else if ('\0' == *pi->cur) {
343
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
344
- return;
345
- } else if ('\\' == *pi->cur) {
346
- read_escaped_str(pi, str);
347
- return;
348
- }
365
+ pi->cur = scan_func(pi->cur, pi->end);
366
+ if (RB_UNLIKELY(pi->end <= pi->cur)) {
367
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
368
+ return;
369
+ }
370
+ if (RB_UNLIKELY('\0' == *pi->cur)) {
371
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
372
+ return;
373
+ }
374
+ if ('\\' == *pi->cur) {
375
+ read_escaped_str(pi, str);
376
+ return;
349
377
  }
378
+
350
379
  if (0 == parent) { // simple add
351
380
  pi->add_cstr(pi, str, pi->cur - str, str);
352
381
  } else {
@@ -370,9 +399,8 @@ static void read_str(ParseInfo pi) {
370
399
  break;
371
400
  case NEXT_HASH_VALUE:
372
401
  pi->hash_set_cstr(pi, parent, str, pi->cur - str, str);
373
- if (0 != parent->key && 0 < parent->klen &&
374
- (parent->key < pi->json || pi->cur < parent->key)) {
375
- xfree((char *)parent->key);
402
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
403
+ OJ_R_FREE((char *)parent->key);
376
404
  parent->key = 0;
377
405
  }
378
406
  parent->next = NEXT_HASH_COMMA;
@@ -398,6 +426,7 @@ static void read_num(ParseInfo pi) {
398
426
  struct _numInfo ni;
399
427
  Val parent = stack_peek(&pi->stack);
400
428
 
429
+ ni.pi = pi;
401
430
  ni.str = pi->cur;
402
431
  ni.i = 0;
403
432
  ni.num = 0;
@@ -414,7 +443,7 @@ static void read_num(ParseInfo pi) {
414
443
  ni.no_big = !pi->options.compat_bigdec;
415
444
  ni.bigdec_load = pi->options.compat_bigdec;
416
445
  } else {
417
- ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
446
+ ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
418
447
  RubyDec == pi->options.bigdec_load);
419
448
  ni.bigdec_load = pi->options.bigdec_load;
420
449
  }
@@ -424,33 +453,21 @@ static void read_num(ParseInfo pi) {
424
453
  ni.neg = 1;
425
454
  } else if ('+' == *pi->cur) {
426
455
  if (StrictMode == pi->options.mode) {
427
- oj_set_error_at(pi,
428
- oj_parse_error_class,
429
- __FILE__,
430
- __LINE__,
431
- "not a number or other value");
456
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
432
457
  return;
433
458
  }
434
459
  pi->cur++;
435
460
  }
436
461
  if ('I' == *pi->cur) {
437
462
  if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
438
- oj_set_error_at(pi,
439
- oj_parse_error_class,
440
- __FILE__,
441
- __LINE__,
442
- "not a number or other value");
463
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
443
464
  return;
444
465
  }
445
466
  pi->cur += 8;
446
467
  ni.infinity = 1;
447
468
  } else if ('N' == *pi->cur || 'n' == *pi->cur) {
448
469
  if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
449
- oj_set_error_at(pi,
450
- oj_parse_error_class,
451
- __FILE__,
452
- __LINE__,
453
- "not a number or other value");
470
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
454
471
  return;
455
472
  }
456
473
  pi->cur += 3;
@@ -459,37 +476,31 @@ static void read_num(ParseInfo pi) {
459
476
  int dec_cnt = 0;
460
477
  bool zero1 = false;
461
478
 
479
+ // Skip leading zeros.
480
+ for (; '0' == *pi->cur; pi->cur++) {
481
+ zero1 = true;
482
+ }
483
+
462
484
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
463
- if (0 == ni.i && '0' == *pi->cur) {
464
- zero1 = true;
465
- }
466
- if (0 < ni.i) {
467
- dec_cnt++;
468
- }
469
- if (!ni.big) {
470
- int d = (*pi->cur - '0');
485
+ int d = (*pi->cur - '0');
471
486
 
472
- if (0 < d) {
473
- if (zero1 && CompatMode == pi->options.mode) {
474
- oj_set_error_at(pi,
475
- oj_parse_error_class,
476
- __FILE__,
477
- __LINE__,
478
- "not a number");
479
- return;
480
- }
481
- zero1 = false;
482
- }
483
- ni.i = ni.i * 10 + d;
484
- if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
485
- ni.big = 1;
486
- }
487
+ if (RB_LIKELY(0 != ni.i)) {
488
+ dec_cnt++;
487
489
  }
490
+ ni.i = ni.i * 10 + d;
491
+ }
492
+ if (RB_UNLIKELY(0 != ni.i && zero1 && CompatMode == pi->options.mode)) {
493
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
494
+ return;
488
495
  }
496
+ if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
497
+ ni.big = true;
498
+ }
499
+
489
500
  if ('.' == *pi->cur) {
490
501
  pi->cur++;
491
502
  // A trailing . is not a valid decimal but if encountered allow it
492
- // except when mimicing the JSON gem or in strict mode.
503
+ // except when mimicking the JSON gem or in strict mode.
493
504
  if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
494
505
  int pos = (int)(pi->cur - ni.str);
495
506
 
@@ -505,25 +516,20 @@ static void read_num(ParseInfo pi) {
505
516
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
506
517
  int d = (*pi->cur - '0');
507
518
 
508
- if (0 < ni.num || 0 < ni.i) {
519
+ if (RB_LIKELY(0 != ni.num || 0 != ni.i)) {
509
520
  dec_cnt++;
510
521
  }
511
- if (INT64_MAX <= ni.div) {
512
- if (!ni.no_big) {
513
- ni.big = true;
514
- }
515
- } else {
516
- ni.num = ni.num * 10 + d;
517
- ni.div *= 10;
518
- ni.di++;
519
- if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
520
- if (!ni.no_big) {
521
- ni.big = true;
522
- }
523
- }
524
- }
522
+ ni.num = ni.num * 10 + d;
523
+ ni.div *= 10;
524
+ ni.di++;
525
525
  }
526
526
  }
527
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
528
+ if (!ni.no_big) {
529
+ ni.big = true;
530
+ }
531
+ }
532
+
527
533
  if ('e' == *pi->cur || 'E' == *pi->cur) {
528
534
  int eneg = 0;
529
535
 
@@ -576,9 +582,8 @@ static void read_num(ParseInfo pi) {
576
582
  break;
577
583
  case NEXT_HASH_VALUE:
578
584
  pi->hash_set_num(pi, parent, &ni);
579
- if (0 != parent->key && 0 < parent->klen &&
580
- (parent->key < pi->json || pi->cur < parent->key)) {
581
- xfree((char *)parent->key);
585
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
586
+ OJ_R_FREE((char *)parent->key);
582
587
  parent->key = 0;
583
588
  }
584
589
  parent->next = NEXT_HASH_COMMA;
@@ -596,7 +601,7 @@ static void read_num(ParseInfo pi) {
596
601
  }
597
602
 
598
603
  static void array_start(ParseInfo pi) {
599
- volatile VALUE v = pi->start_array(pi);
604
+ VALUE v = pi->start_array(pi);
600
605
 
601
606
  stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
602
607
  }
@@ -620,13 +625,13 @@ static void array_end(ParseInfo pi) {
620
625
  }
621
626
 
622
627
  static void hash_start(ParseInfo pi) {
623
- volatile VALUE v = pi->start_hash(pi);
628
+ VALUE v = pi->start_hash(pi);
624
629
 
625
630
  stack_push(&pi->stack, v, NEXT_HASH_NEW);
626
631
  }
627
632
 
628
633
  static void hash_end(ParseInfo pi) {
629
- volatile Val hash = stack_peek(&pi->stack);
634
+ Val hash = stack_peek(&pi->stack);
630
635
 
631
636
  // leave hash on stack until just before
632
637
  if (0 == hash) {
@@ -705,17 +710,10 @@ void oj_parse2(ParseInfo pi) {
705
710
  case '[': array_start(pi); break;
706
711
  case ']': array_end(pi); break;
707
712
  case ',': comma(pi); break;
708
- case '"':
709
- read_str(pi);
710
- break;
711
- // case '+':
713
+ case '"': read_str(pi); break;
712
714
  case '+':
713
715
  if (CompatMode == pi->options.mode) {
714
- oj_set_error_at(pi,
715
- oj_parse_error_class,
716
- __FILE__,
717
- __LINE__,
718
- "unexpected character");
716
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
719
717
  return;
720
718
  }
721
719
  pi->cur--;
@@ -741,11 +739,7 @@ void oj_parse2(ParseInfo pi) {
741
739
  pi->cur--;
742
740
  read_num(pi);
743
741
  } else {
744
- oj_set_error_at(pi,
745
- oj_parse_error_class,
746
- __FILE__,
747
- __LINE__,
748
- "unexpected character");
742
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
749
743
  }
750
744
  break;
751
745
  case 't': read_true(pi); break;
@@ -765,9 +759,7 @@ void oj_parse2(ParseInfo pi) {
765
759
  }
766
760
  break;
767
761
  case '\0': pi->cur--; return;
768
- default:
769
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
770
- return;
762
+ default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character"); return;
771
763
  }
772
764
  if (err_has(&pi->err)) {
773
765
  return;
@@ -804,16 +796,15 @@ static VALUE parse_big_decimal(VALUE str) {
804
796
  }
805
797
 
806
798
  static long double exp_plus[] = {
807
- 1.0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
808
- 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19,
809
- 1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29,
810
- 1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, 1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39,
811
- 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
799
+ 1.0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, 1.0e11, 1.0e12,
800
+ 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25,
801
+ 1.0e26, 1.0e27, 1.0e28, 1.0e29, 1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, 1.0e35, 1.0e36, 1.0e37, 1.0e38,
802
+ 1.0e39, 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
812
803
  };
813
804
 
814
805
  VALUE
815
806
  oj_num_as_value(NumInfo ni) {
816
- volatile VALUE rnum = Qnil;
807
+ VALUE rnum = Qnil;
817
808
 
818
809
  if (ni->infinity) {
819
810
  if (ni->neg) {
@@ -832,12 +823,12 @@ oj_num_as_value(NumInfo ni) {
832
823
  buf[ni->len] = '\0';
833
824
  rnum = rb_cstr_to_inum(buf, 10, 0);
834
825
  } else {
835
- char *buf = ALLOC_N(char, ni->len + 1);
826
+ char *buf = OJ_R_ALLOC_N(char, ni->len + 1);
836
827
 
837
828
  memcpy(buf, ni->str, ni->len);
838
829
  buf[ni->len] = '\0';
839
830
  rnum = rb_cstr_to_inum(buf, 10, 0);
840
- xfree(buf);
831
+ OJ_R_FREE(buf);
841
832
  }
842
833
  } else {
843
834
  if (ni->neg) {
@@ -848,7 +839,7 @@ oj_num_as_value(NumInfo ni) {
848
839
  }
849
840
  } else { // decimal
850
841
  if (ni->big) {
851
- volatile VALUE bd = rb_str_new(ni->str, ni->len);
842
+ VALUE bd = rb_str_new(ni->str, ni->len);
852
843
 
853
844
  rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
854
845
  if (ni->no_big) {
@@ -876,15 +867,19 @@ oj_num_as_value(NumInfo ni) {
876
867
  }
877
868
  rnum = rb_float_new((double)ld);
878
869
  } else if (RubyDec == ni->bigdec_load) {
879
- volatile VALUE sv = rb_str_new(ni->str, ni->len);
870
+ VALUE sv = rb_str_new(ni->str, ni->len);
880
871
 
881
872
  rnum = rb_funcall(sv, rb_intern("to_f"), 0);
882
873
  } else {
883
- char * end;
874
+ char *end;
884
875
  double d = strtod(ni->str, &end);
885
876
 
886
877
  if ((long)ni->len != (long)(end - ni->str)) {
887
- rb_raise(oj_parse_error_class, "Invalid float");
878
+ if (Qnil == ni->pi->err_class) {
879
+ rb_raise(oj_parse_error_class, "Invalid float");
880
+ } else {
881
+ rb_raise(ni->pi->err_class, "Invalid float");
882
+ }
888
883
  }
889
884
  rnum = rb_float_new(d);
890
885
  }
@@ -892,21 +887,24 @@ oj_num_as_value(NumInfo ni) {
892
887
  return rnum;
893
888
  }
894
889
 
895
- void oj_set_error_at(ParseInfo pi,
896
- VALUE err_clas,
897
- const char *file,
898
- int line,
899
- const char *format,
900
- ...) {
890
+ void oj_set_error_at(ParseInfo pi, VALUE err_clas, const char *file, int line, const char *format, ...) {
901
891
  va_list ap;
902
892
  char msg[256];
903
- char * p = msg;
904
- char * end = p + sizeof(msg) - 2;
905
- char * start;
893
+ char *p = msg;
894
+ char *end = p + sizeof(msg) - 2;
895
+ char *start;
906
896
  Val vp;
897
+ int mlen;
907
898
 
908
899
  va_start(ap, format);
909
- p += vsnprintf(msg, sizeof(msg) - 1, format, ap);
900
+ mlen = vsnprintf(msg, sizeof(msg) - 1, format, ap);
901
+ if (0 < mlen) {
902
+ if (sizeof(msg) - 2 < (size_t)mlen) {
903
+ p = end - 2;
904
+ } else {
905
+ p += mlen;
906
+ }
907
+ }
910
908
  va_end(ap);
911
909
  pi->err.clas = err_clas;
912
910
  if (p + 3 < end) {
@@ -942,14 +940,7 @@ void oj_set_error_at(ParseInfo pi,
942
940
  }
943
941
  *p = '\0';
944
942
  if (0 == pi->json) {
945
- oj_err_set(&pi->err,
946
- err_clas,
947
- "%s at line %d, column %d [%s:%d]",
948
- msg,
949
- pi->rd.line,
950
- pi->rd.col,
951
- file,
952
- line);
943
+ oj_err_set(&pi->err, err_clas, "%s at line %d, column %d [%s:%d]", msg, pi->rd.line, pi->rd.col, file, line);
953
944
  } else {
954
945
  _oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
955
946
  }
@@ -963,24 +954,25 @@ static VALUE protect_parse(VALUE pip) {
963
954
 
964
955
  extern int oj_utf8_index;
965
956
 
966
- static void oj_pi_set_input_str(ParseInfo pi, volatile VALUE *inputp) {
967
- rb_encoding *enc = rb_to_encoding(rb_obj_encoding(*inputp));
957
+ static void oj_pi_set_input_str(ParseInfo pi, VALUE *inputp) {
958
+ int idx = RB_ENCODING_GET(*inputp);
968
959
 
969
- if (rb_utf8_encoding() != enc) {
970
- *inputp = rb_str_conv_enc(*inputp, enc, rb_utf8_encoding());
960
+ if (oj_utf8_encoding_index != idx) {
961
+ rb_encoding *enc = rb_enc_from_index(idx);
962
+ *inputp = rb_str_conv_enc(*inputp, enc, oj_utf8_encoding);
971
963
  }
972
- pi->json = rb_string_value_ptr((VALUE *)inputp);
964
+ pi->json = RSTRING_PTR(*inputp);
973
965
  pi->end = pi->json + RSTRING_LEN(*inputp);
974
966
  }
975
967
 
976
968
  VALUE
977
969
  oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
978
- char * buf = 0;
979
- volatile VALUE input;
980
- volatile VALUE wrapped_stack;
981
- volatile VALUE result = Qnil;
982
- int line = 0;
983
- int free_json = 0;
970
+ char *buf = 0;
971
+ VALUE input;
972
+ VALUE wrapped_stack;
973
+ VALUE result = Qnil;
974
+ int line = 0;
975
+ int free_json = 0;
984
976
 
985
977
  if (argc < 1) {
986
978
  rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
@@ -1016,8 +1008,8 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1016
1008
  rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
1017
1009
  }
1018
1010
  } else {
1019
- VALUE clas = rb_obj_class(input);
1020
- volatile VALUE s;
1011
+ VALUE clas = rb_obj_class(input);
1012
+ VALUE s;
1021
1013
 
1022
1014
  if (oj_stringio_class == clas) {
1023
1015
  s = rb_funcall2(input, oj_string_id, 0, 0);
@@ -1029,19 +1021,18 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1029
1021
  size_t len = lseek(fd, 0, SEEK_END);
1030
1022
 
1031
1023
  lseek(fd, 0, SEEK_SET);
1032
- buf = ALLOC_N(char, len + 1);
1024
+ buf = OJ_R_ALLOC_N(char, len + 1);
1033
1025
  pi->json = buf;
1034
1026
  pi->end = buf + len;
1035
1027
  if (0 >= (cnt = read(fd, (char *)pi->json, len)) || cnt != (ssize_t)len) {
1036
1028
  if (0 != buf) {
1037
- xfree(buf);
1029
+ OJ_R_FREE(buf);
1038
1030
  }
1039
1031
  rb_raise(rb_eIOError, "failed to read from IO Object.");
1040
1032
  }
1041
1033
  ((char *)pi->json)[len] = '\0';
1042
1034
  /* skip UTF-8 BOM if present */
1043
- if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] &&
1044
- 0xBF == (uint8_t)pi->json[2]) {
1035
+ if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
1045
1036
  pi->cur += 3;
1046
1037
  }
1047
1038
  #endif
@@ -1067,8 +1058,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1067
1058
  wrapped_stack = oj_stack_init(&pi->stack);
1068
1059
  rb_protect(protect_parse, (VALUE)pi, &line);
1069
1060
  if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
1070
- if (No == pi->options.nilnil ||
1071
- (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
1061
+ if (No == pi->options.nilnil || (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
1072
1062
  oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
1073
1063
  }
1074
1064
  }
@@ -1096,9 +1086,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1096
1086
  switch (v->next) {
1097
1087
  case NEXT_ARRAY_NEW:
1098
1088
  case NEXT_ARRAY_ELEMENT:
1099
- case NEXT_ARRAY_COMMA:
1100
- oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated");
1101
- break;
1089
+ case NEXT_ARRAY_COMMA: oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated"); break;
1102
1090
  case NEXT_HASH_NEW:
1103
1091
  case NEXT_HASH_KEY:
1104
1092
  case NEXT_HASH_COLON:
@@ -1116,9 +1104,9 @@ CLEANUP:
1116
1104
  oj_circ_array_free(pi->circ_array);
1117
1105
  }
1118
1106
  if (0 != buf) {
1119
- xfree(buf);
1107
+ OJ_R_FREE(buf);
1120
1108
  } else if (free_json) {
1121
- xfree(json);
1109
+ OJ_R_FREE(json);
1122
1110
  }
1123
1111
  stack_cleanup(&pi->stack);
1124
1112
  if (pi->str_rx.head != oj_default_options.str_rx.head) {
@@ -1129,7 +1117,7 @@ CLEANUP:
1129
1117
  if (Qnil != pi->err_class) {
1130
1118
  pi->err.clas = pi->err_class;
1131
1119
  }
1132
- if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
1120
+ if ((CompatMode == pi->options.mode || RailsMode == pi->options.mode) && Yes != pi->options.safe) {
1133
1121
  // The json gem requires the error message be UTF-8 encoded. In
1134
1122
  // additional the complete JSON source must be returned. There
1135
1123
  // does not seem to be a size limit.