oj 3.13.9 → 3.16.1

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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -0
  3. data/README.md +13 -2
  4. data/ext/oj/buf.h +11 -6
  5. data/ext/oj/cache.c +25 -24
  6. data/ext/oj/cache8.c +10 -9
  7. data/ext/oj/circarray.c +8 -6
  8. data/ext/oj/circarray.h +2 -2
  9. data/ext/oj/code.c +19 -33
  10. data/ext/oj/code.h +2 -2
  11. data/ext/oj/compat.c +20 -60
  12. data/ext/oj/custom.c +76 -155
  13. data/ext/oj/debug.c +3 -9
  14. data/ext/oj/dump.c +203 -213
  15. data/ext/oj/dump.h +26 -12
  16. data/ext/oj/dump_compat.c +565 -642
  17. data/ext/oj/dump_leaf.c +17 -63
  18. data/ext/oj/dump_object.c +59 -181
  19. data/ext/oj/dump_strict.c +24 -48
  20. data/ext/oj/encoder.c +43 -0
  21. data/ext/oj/err.c +2 -13
  22. data/ext/oj/err.h +9 -12
  23. data/ext/oj/extconf.rb +18 -7
  24. data/ext/oj/fast.c +83 -108
  25. data/ext/oj/intern.c +52 -50
  26. data/ext/oj/intern.h +4 -8
  27. data/ext/oj/mem.c +318 -0
  28. data/ext/oj/mem.h +53 -0
  29. data/ext/oj/mimic_json.c +104 -81
  30. data/ext/oj/object.c +50 -67
  31. data/ext/oj/odd.c +89 -67
  32. data/ext/oj/odd.h +15 -15
  33. data/ext/oj/oj.c +171 -106
  34. data/ext/oj/oj.h +96 -74
  35. data/ext/oj/parse.c +169 -189
  36. data/ext/oj/parse.h +23 -24
  37. data/ext/oj/parser.c +89 -34
  38. data/ext/oj/parser.h +20 -9
  39. data/ext/oj/rails.c +86 -151
  40. data/ext/oj/rails.h +1 -1
  41. data/ext/oj/reader.c +12 -15
  42. data/ext/oj/reader.h +4 -2
  43. data/ext/oj/resolve.c +3 -4
  44. data/ext/oj/rxclass.c +6 -5
  45. data/ext/oj/rxclass.h +1 -1
  46. data/ext/oj/saj.c +21 -32
  47. data/ext/oj/saj2.c +329 -93
  48. data/ext/oj/saj2.h +23 -0
  49. data/ext/oj/scp.c +3 -14
  50. data/ext/oj/sparse.c +26 -70
  51. data/ext/oj/stream_writer.c +12 -22
  52. data/ext/oj/strict.c +20 -52
  53. data/ext/oj/string_writer.c +21 -22
  54. data/ext/oj/trace.h +31 -4
  55. data/ext/oj/usual.c +105 -150
  56. data/ext/oj/usual.h +68 -0
  57. data/ext/oj/util.h +1 -1
  58. data/ext/oj/val_stack.c +1 -1
  59. data/ext/oj/val_stack.h +8 -7
  60. data/ext/oj/validate.c +21 -26
  61. data/ext/oj/wab.c +32 -69
  62. data/lib/oj/active_support_helper.rb +1 -3
  63. data/lib/oj/bag.rb +7 -1
  64. data/lib/oj/easy_hash.rb +4 -5
  65. data/lib/oj/error.rb +0 -1
  66. data/lib/oj/json.rb +162 -150
  67. data/lib/oj/mimic.rb +6 -2
  68. data/lib/oj/saj.rb +20 -6
  69. data/lib/oj/state.rb +9 -6
  70. data/lib/oj/version.rb +1 -2
  71. data/lib/oj.rb +2 -0
  72. data/pages/Compatibility.md +1 -1
  73. data/pages/InstallOptions.md +20 -0
  74. data/pages/JsonGem.md +15 -0
  75. data/pages/Modes.md +6 -3
  76. data/pages/Options.md +10 -0
  77. data/pages/Rails.md +12 -0
  78. data/test/_test_active.rb +8 -9
  79. data/test/_test_active_mimic.rb +7 -8
  80. data/test/_test_mimic_rails.rb +17 -20
  81. data/test/activerecord/result_test.rb +5 -6
  82. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  83. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  84. data/test/{activesupport5 → activesupport7}/encoding_test.rb +20 -34
  85. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  86. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  87. data/test/files.rb +15 -15
  88. data/test/foo.rb +15 -15
  89. data/test/helper.rb +11 -8
  90. data/test/isolated/shared.rb +3 -2
  91. data/test/json_gem/json_addition_test.rb +2 -2
  92. data/test/json_gem/json_common_interface_test.rb +8 -6
  93. data/test/json_gem/json_encoding_test.rb +0 -0
  94. data/test/json_gem/json_ext_parser_test.rb +1 -0
  95. data/test/json_gem/json_fixtures_test.rb +3 -2
  96. data/test/json_gem/json_generator_test.rb +49 -37
  97. data/test/json_gem/json_generic_object_test.rb +11 -11
  98. data/test/json_gem/json_parser_test.rb +54 -47
  99. data/test/json_gem/json_string_matching_test.rb +9 -9
  100. data/test/json_gem/test_helper.rb +7 -3
  101. data/test/mem.rb +13 -12
  102. data/test/perf.rb +21 -26
  103. data/test/perf_compat.rb +31 -33
  104. data/test/perf_dump.rb +50 -0
  105. data/test/perf_fast.rb +80 -82
  106. data/test/perf_file.rb +27 -29
  107. data/test/perf_object.rb +65 -69
  108. data/test/perf_once.rb +12 -11
  109. data/test/perf_parser.rb +42 -48
  110. data/test/perf_saj.rb +46 -54
  111. data/test/perf_scp.rb +57 -69
  112. data/test/perf_simple.rb +41 -39
  113. data/test/perf_strict.rb +68 -70
  114. data/test/perf_wab.rb +67 -69
  115. data/test/prec.rb +3 -3
  116. data/test/sample/change.rb +0 -1
  117. data/test/sample/dir.rb +0 -1
  118. data/test/sample/doc.rb +0 -1
  119. data/test/sample/file.rb +0 -1
  120. data/test/sample/group.rb +0 -1
  121. data/test/sample/hasprops.rb +0 -1
  122. data/test/sample/layer.rb +0 -1
  123. data/test/sample/rect.rb +0 -1
  124. data/test/sample/shape.rb +0 -1
  125. data/test/sample/text.rb +0 -1
  126. data/test/sample.rb +16 -16
  127. data/test/sample_json.rb +8 -8
  128. data/test/test_compat.rb +95 -43
  129. data/test/test_custom.rb +72 -51
  130. data/test/test_debian.rb +7 -10
  131. data/test/test_fast.rb +102 -87
  132. data/test/test_file.rb +41 -30
  133. data/test/test_gc.rb +16 -5
  134. data/test/test_generate.rb +5 -5
  135. data/test/test_hash.rb +4 -4
  136. data/test/test_integer_range.rb +9 -9
  137. data/test/test_null.rb +20 -20
  138. data/test/test_object.rb +85 -96
  139. data/test/test_parser.rb +6 -22
  140. data/test/test_parser_debug.rb +27 -0
  141. data/test/test_parser_saj.rb +115 -23
  142. data/test/test_parser_usual.rb +6 -6
  143. data/test/test_rails.rb +2 -2
  144. data/test/test_saj.rb +10 -8
  145. data/test/test_scp.rb +37 -39
  146. data/test/test_strict.rb +40 -32
  147. data/test/test_various.rb +163 -84
  148. data/test/test_wab.rb +48 -44
  149. data/test/test_writer.rb +47 -47
  150. data/test/tests.rb +13 -5
  151. data/test/tests_mimic.rb +12 -3
  152. data/test/tests_mimic_addition.rb +12 -3
  153. metadata +34 -144
  154. data/test/activesupport4/decoding_test.rb +0 -108
  155. data/test/activesupport4/encoding_test.rb +0 -531
  156. data/test/activesupport4/test_helper.rb +0 -41
  157. data/test/activesupport5/test_helper.rb +0 -72
  158. data/test/bar.rb +0 -16
  159. data/test/baz.rb +0 -16
  160. data/test/bug.rb +0 -16
  161. 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;
349
373
  }
374
+ if ('\\' == *pi->cur) {
375
+ read_escaped_str(pi, str);
376
+ return;
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,33 +476,27 @@ 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;
488
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;
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
@@ -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
+ }
526
+ }
527
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
528
+ if (!ni.no_big) {
529
+ ni.big = true;
525
530
  }
526
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,28 +887,23 @@ 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;
907
- int mlen;
897
+ int mlen;
908
898
 
909
899
  va_start(ap, format);
910
900
  mlen = vsnprintf(msg, sizeof(msg) - 1, format, ap);
911
901
  if (0 < mlen) {
912
- if (sizeof(msg) - 2 < (size_t)mlen) {
913
- p = end - 2;
914
- } else {
915
- p += mlen;
916
- }
902
+ if (sizeof(msg) - 2 < (size_t)mlen) {
903
+ p = end - 2;
904
+ } else {
905
+ p += mlen;
906
+ }
917
907
  }
918
908
  va_end(ap);
919
909
  pi->err.clas = err_clas;
@@ -950,14 +940,7 @@ void oj_set_error_at(ParseInfo pi,
950
940
  }
951
941
  *p = '\0';
952
942
  if (0 == pi->json) {
953
- oj_err_set(&pi->err,
954
- err_clas,
955
- "%s at line %d, column %d [%s:%d]",
956
- msg,
957
- pi->rd.line,
958
- pi->rd.col,
959
- file,
960
- 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);
961
944
  } else {
962
945
  _oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
963
946
  }
@@ -971,11 +954,12 @@ static VALUE protect_parse(VALUE pip) {
971
954
 
972
955
  extern int oj_utf8_index;
973
956
 
974
- static void oj_pi_set_input_str(ParseInfo pi, volatile VALUE *inputp) {
975
- rb_encoding *enc = rb_enc_get(*inputp);
957
+ static void oj_pi_set_input_str(ParseInfo pi, VALUE *inputp) {
958
+ int idx = RB_ENCODING_GET(*inputp);
976
959
 
977
- if (oj_utf8_encoding != enc) {
978
- *inputp = rb_str_conv_enc(*inputp, enc, oj_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);
979
963
  }
980
964
  pi->json = RSTRING_PTR(*inputp);
981
965
  pi->end = pi->json + RSTRING_LEN(*inputp);
@@ -983,12 +967,12 @@ static void oj_pi_set_input_str(ParseInfo pi, volatile VALUE *inputp) {
983
967
 
984
968
  VALUE
985
969
  oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
986
- char * buf = 0;
987
- volatile VALUE input;
988
- volatile VALUE wrapped_stack;
989
- volatile VALUE result = Qnil;
990
- int line = 0;
991
- 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;
992
976
 
993
977
  if (argc < 1) {
994
978
  rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
@@ -1024,8 +1008,8 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1024
1008
  rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
1025
1009
  }
1026
1010
  } else {
1027
- VALUE clas = rb_obj_class(input);
1028
- volatile VALUE s;
1011
+ VALUE clas = rb_obj_class(input);
1012
+ VALUE s;
1029
1013
 
1030
1014
  if (oj_stringio_class == clas) {
1031
1015
  s = rb_funcall2(input, oj_string_id, 0, 0);
@@ -1037,19 +1021,18 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1037
1021
  size_t len = lseek(fd, 0, SEEK_END);
1038
1022
 
1039
1023
  lseek(fd, 0, SEEK_SET);
1040
- buf = ALLOC_N(char, len + 1);
1024
+ buf = OJ_R_ALLOC_N(char, len + 1);
1041
1025
  pi->json = buf;
1042
1026
  pi->end = buf + len;
1043
1027
  if (0 >= (cnt = read(fd, (char *)pi->json, len)) || cnt != (ssize_t)len) {
1044
1028
  if (0 != buf) {
1045
- xfree(buf);
1029
+ OJ_R_FREE(buf);
1046
1030
  }
1047
1031
  rb_raise(rb_eIOError, "failed to read from IO Object.");
1048
1032
  }
1049
1033
  ((char *)pi->json)[len] = '\0';
1050
1034
  /* skip UTF-8 BOM if present */
1051
- if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] &&
1052
- 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]) {
1053
1036
  pi->cur += 3;
1054
1037
  }
1055
1038
  #endif
@@ -1075,8 +1058,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1075
1058
  wrapped_stack = oj_stack_init(&pi->stack);
1076
1059
  rb_protect(protect_parse, (VALUE)pi, &line);
1077
1060
  if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
1078
- if (No == pi->options.nilnil ||
1079
- (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
1061
+ if (No == pi->options.nilnil || (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
1080
1062
  oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
1081
1063
  }
1082
1064
  }
@@ -1104,9 +1086,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
1104
1086
  switch (v->next) {
1105
1087
  case NEXT_ARRAY_NEW:
1106
1088
  case NEXT_ARRAY_ELEMENT:
1107
- case NEXT_ARRAY_COMMA:
1108
- oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated");
1109
- break;
1089
+ case NEXT_ARRAY_COMMA: oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated"); break;
1110
1090
  case NEXT_HASH_NEW:
1111
1091
  case NEXT_HASH_KEY:
1112
1092
  case NEXT_HASH_COLON:
@@ -1124,9 +1104,9 @@ CLEANUP:
1124
1104
  oj_circ_array_free(pi->circ_array);
1125
1105
  }
1126
1106
  if (0 != buf) {
1127
- xfree(buf);
1107
+ OJ_R_FREE(buf);
1128
1108
  } else if (free_json) {
1129
- xfree(json);
1109
+ OJ_R_FREE(json);
1130
1110
  }
1131
1111
  stack_cleanup(&pi->stack);
1132
1112
  if (pi->str_rx.head != oj_default_options.str_rx.head) {