oj 3.7.4 → 3.13.21

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1352 -0
  3. data/README.md +29 -8
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +53 -72
  6. data/ext/oj/cache.c +326 -0
  7. data/ext/oj/cache.h +21 -0
  8. data/ext/oj/cache8.c +61 -64
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +37 -43
  11. data/ext/oj/circarray.h +16 -17
  12. data/ext/oj/code.c +165 -179
  13. data/ext/oj/code.h +27 -29
  14. data/ext/oj/compat.c +174 -194
  15. data/ext/oj/custom.c +809 -866
  16. data/ext/oj/debug.c +132 -0
  17. data/ext/oj/dump.c +848 -863
  18. data/ext/oj/dump.h +81 -67
  19. data/ext/oj/dump_compat.c +85 -123
  20. data/ext/oj/dump_leaf.c +100 -188
  21. data/ext/oj/dump_object.c +527 -656
  22. data/ext/oj/dump_strict.c +315 -338
  23. data/ext/oj/encode.h +7 -34
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +40 -29
  26. data/ext/oj/err.h +48 -48
  27. data/ext/oj/extconf.rb +17 -4
  28. data/ext/oj/fast.c +1070 -1087
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +469 -436
  32. data/ext/oj/object.c +525 -593
  33. data/ext/oj/odd.c +154 -138
  34. data/ext/oj/odd.h +37 -38
  35. data/ext/oj/oj.c +1325 -986
  36. data/ext/oj/oj.h +333 -316
  37. data/ext/oj/parse.c +1002 -846
  38. data/ext/oj/parse.h +92 -87
  39. data/ext/oj/parser.c +1557 -0
  40. data/ext/oj/parser.h +91 -0
  41. data/ext/oj/rails.c +888 -878
  42. data/ext/oj/rails.h +11 -14
  43. data/ext/oj/reader.c +141 -147
  44. data/ext/oj/reader.h +73 -89
  45. data/ext/oj/resolve.c +41 -62
  46. data/ext/oj/resolve.h +7 -9
  47. data/ext/oj/rxclass.c +71 -75
  48. data/ext/oj/rxclass.h +18 -19
  49. data/ext/oj/saj.c +443 -486
  50. data/ext/oj/saj2.c +602 -0
  51. data/ext/oj/scp.c +88 -113
  52. data/ext/oj/sparse.c +787 -709
  53. data/ext/oj/stream_writer.c +133 -159
  54. data/ext/oj/strict.c +127 -118
  55. data/ext/oj/string_writer.c +230 -249
  56. data/ext/oj/trace.c +34 -41
  57. data/ext/oj/trace.h +19 -19
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +59 -67
  62. data/ext/oj/val_stack.h +91 -129
  63. data/ext/oj/validate.c +46 -0
  64. data/ext/oj/wab.c +342 -353
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +5 -4
  67. data/lib/oj/error.rb +1 -1
  68. data/lib/oj/json.rb +1 -1
  69. data/lib/oj/mimic.rb +48 -14
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/state.rb +8 -7
  72. data/lib/oj/version.rb +2 -2
  73. data/lib/oj.rb +0 -8
  74. data/pages/Compatibility.md +1 -1
  75. data/pages/JsonGem.md +15 -0
  76. data/pages/Modes.md +53 -46
  77. data/pages/Options.md +72 -11
  78. data/pages/Parser.md +309 -0
  79. data/pages/Rails.md +73 -22
  80. data/pages/Security.md +1 -1
  81. data/test/activerecord/result_test.rb +7 -2
  82. data/test/activesupport5/abstract_unit.rb +45 -0
  83. data/test/activesupport5/decoding_test.rb +68 -60
  84. data/test/activesupport5/encoding_test.rb +111 -96
  85. data/test/activesupport5/encoding_test_cases.rb +33 -25
  86. data/test/activesupport5/test_helper.rb +43 -21
  87. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  88. data/test/activesupport6/abstract_unit.rb +44 -0
  89. data/test/activesupport6/decoding_test.rb +133 -0
  90. data/test/activesupport6/encoding_test.rb +507 -0
  91. data/test/activesupport6/encoding_test_cases.rb +98 -0
  92. data/test/activesupport6/test_common.rb +17 -0
  93. data/test/activesupport6/test_helper.rb +163 -0
  94. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  95. data/test/activesupport7/abstract_unit.rb +49 -0
  96. data/test/activesupport7/decoding_test.rb +125 -0
  97. data/test/activesupport7/encoding_test.rb +486 -0
  98. data/test/activesupport7/encoding_test_cases.rb +104 -0
  99. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  100. data/test/bar.rb +6 -12
  101. data/test/baz.rb +16 -0
  102. data/test/bug.rb +16 -0
  103. data/test/foo.rb +69 -75
  104. data/test/helper.rb +16 -0
  105. data/test/json_gem/json_common_interface_test.rb +8 -3
  106. data/test/json_gem/json_generator_test.rb +18 -4
  107. data/test/json_gem/json_parser_test.rb +9 -0
  108. data/test/json_gem/test_helper.rb +12 -0
  109. data/test/mem.rb +33 -0
  110. data/test/perf.rb +1 -1
  111. data/test/perf_dump.rb +50 -0
  112. data/test/perf_once.rb +58 -0
  113. data/test/perf_parser.rb +189 -0
  114. data/test/perf_scp.rb +11 -10
  115. data/test/perf_strict.rb +17 -23
  116. data/test/prec.rb +23 -0
  117. data/test/sample_json.rb +1 -1
  118. data/test/test_compat.rb +46 -10
  119. data/test/test_custom.rb +147 -8
  120. data/test/test_fast.rb +62 -2
  121. data/test/test_file.rb +25 -2
  122. data/test/test_gc.rb +13 -0
  123. data/test/test_generate.rb +21 -0
  124. data/test/test_hash.rb +11 -1
  125. data/test/test_integer_range.rb +7 -2
  126. data/test/test_object.rb +85 -9
  127. data/test/test_parser.rb +27 -0
  128. data/test/test_parser_saj.rb +335 -0
  129. data/test/test_parser_usual.rb +217 -0
  130. data/test/test_rails.rb +35 -0
  131. data/test/test_saj.rb +1 -1
  132. data/test/test_scp.rb +5 -5
  133. data/test/test_strict.rb +26 -1
  134. data/test/test_various.rb +87 -65
  135. data/test/test_wab.rb +2 -0
  136. data/test/test_writer.rb +19 -2
  137. data/test/tests.rb +1 -1
  138. data/test/zoo.rb +13 -0
  139. metadata +60 -110
  140. data/ext/oj/hash.c +0 -163
  141. data/ext/oj/hash.h +0 -46
  142. data/ext/oj/hash_test.c +0 -512
data/ext/oj/parse.c CHANGED
@@ -1,853 +1,1004 @@
1
- /* parse.c
2
- * Copyright (c) 2013, Peter Ohler
3
- * All rights reserved.
4
- */
1
+ // Copyright (c) 2013 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
5
3
 
6
- #include <stdlib.h>
4
+ #include "parse.h"
5
+
6
+ #include <math.h>
7
+ #include <ruby/util.h>
7
8
  #include <stdio.h>
9
+ #include <stdlib.h>
8
10
  #include <string.h>
9
11
  #include <unistd.h>
10
- #include <math.h>
11
12
 
12
- #include "oj.h"
13
- #include "encode.h"
14
- #include "parse.h"
15
13
  #include "buf.h"
16
- #include "val_stack.h"
14
+ #include "encode.h"
15
+ #include "oj.h"
17
16
  #include "rxclass.h"
17
+ #include "val_stack.h"
18
18
 
19
19
  // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
20
- #define OJ_INFINITY (1.0/0.0)
20
+ #define OJ_INFINITY (1.0 / 0.0)
21
21
 
22
22
  //#define EXP_MAX 1023
23
- #define EXP_MAX 100000
24
- #define DEC_MAX 15
23
+ #define EXP_MAX 100000
24
+ #define DEC_MAX 15
25
25
 
26
- static void
27
- next_non_white(ParseInfo pi) {
26
+ static void next_non_white(ParseInfo pi) {
28
27
  for (; 1; pi->cur++) {
29
- switch(*pi->cur) {
30
- case ' ':
31
- case '\t':
32
- case '\f':
33
- case '\n':
34
- case '\r':
35
- break;
36
- default:
37
- return;
38
- }
28
+ switch (*pi->cur) {
29
+ case ' ':
30
+ case '\t':
31
+ case '\f':
32
+ case '\n':
33
+ case '\r': break;
34
+ default: return;
35
+ }
39
36
  }
40
37
  }
41
38
 
42
- static void
43
- skip_comment(ParseInfo pi) {
39
+ static void skip_comment(ParseInfo pi) {
44
40
  if ('*' == *pi->cur) {
45
- pi->cur++;
46
- for (; pi->cur < pi->end; pi->cur++) {
47
- if ('*' == *pi->cur && '/' == *(pi->cur + 1)) {
48
- pi->cur += 2;
49
- return;
50
- } else if (pi->end <= pi->cur) {
51
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
52
- return;
53
- }
54
- }
41
+ pi->cur++;
42
+ for (; pi->cur < pi->end; pi->cur++) {
43
+ if ('*' == *pi->cur && '/' == *(pi->cur + 1)) {
44
+ pi->cur += 2;
45
+ return;
46
+ } 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
+ return;
53
+ }
54
+ }
55
55
  } else if ('/' == *pi->cur) {
56
- for (; 1; pi->cur++) {
57
- switch (*pi->cur) {
58
- case '\n':
59
- case '\r':
60
- case '\f':
61
- case '\0':
62
- return;
63
- default:
64
- break;
65
- }
66
- }
56
+ for (; 1; pi->cur++) {
57
+ switch (*pi->cur) {
58
+ case '\n':
59
+ case '\r':
60
+ case '\f':
61
+ case '\0': return;
62
+ default: break;
63
+ }
64
+ }
67
65
  } else {
68
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
66
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
69
67
  }
70
68
  }
71
69
 
72
- static void
73
- add_value(ParseInfo pi, VALUE rval) {
74
- Val parent = stack_peek(&pi->stack);
70
+ static void add_value(ParseInfo pi, VALUE rval) {
71
+ Val parent = stack_peek(&pi->stack);
75
72
 
76
- if (0 == parent) { // simple add
77
- pi->add_value(pi, rval);
73
+ if (0 == parent) { // simple add
74
+ pi->add_value(pi, rval);
78
75
  } else {
79
- switch (parent->next) {
80
- case NEXT_ARRAY_NEW:
81
- case NEXT_ARRAY_ELEMENT:
82
- pi->array_append_value(pi, rval);
83
- parent->next = NEXT_ARRAY_COMMA;
84
- break;
85
- case NEXT_HASH_VALUE:
86
- pi->hash_set_value(pi, parent, rval);
87
- if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
88
- xfree((char*)parent->key);
89
- parent->key = 0;
90
- }
91
- parent->next = NEXT_HASH_COMMA;
92
- break;
93
- case NEXT_HASH_NEW:
94
- case NEXT_HASH_KEY:
95
- case NEXT_HASH_COMMA:
96
- case NEXT_NONE:
97
- case NEXT_ARRAY_COMMA:
98
- case NEXT_HASH_COLON:
99
- default:
100
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
101
- break;
102
- }
76
+ switch (parent->next) {
77
+ case NEXT_ARRAY_NEW:
78
+ case NEXT_ARRAY_ELEMENT:
79
+ pi->array_append_value(pi, rval);
80
+ parent->next = NEXT_ARRAY_COMMA;
81
+ break;
82
+ case NEXT_HASH_VALUE:
83
+ 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);
87
+ parent->key = 0;
88
+ }
89
+ parent->next = NEXT_HASH_COMMA;
90
+ break;
91
+ case NEXT_HASH_NEW:
92
+ case NEXT_HASH_KEY:
93
+ case NEXT_HASH_COMMA:
94
+ case NEXT_NONE:
95
+ case NEXT_ARRAY_COMMA:
96
+ case NEXT_HASH_COLON:
97
+ default:
98
+ oj_set_error_at(pi,
99
+ oj_parse_error_class,
100
+ __FILE__,
101
+ __LINE__,
102
+ "expected %s",
103
+ oj_stack_next_string(parent->next));
104
+ break;
105
+ }
103
106
  }
104
107
  }
105
108
 
106
- static void
107
- read_null(ParseInfo pi) {
109
+ static void read_null(ParseInfo pi) {
108
110
  if ('u' == *pi->cur++ && 'l' == *pi->cur++ && 'l' == *pi->cur++) {
109
- add_value(pi, Qnil);
111
+ add_value(pi, Qnil);
110
112
  } else {
111
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
113
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
112
114
  }
113
115
  }
114
116
 
115
- static void
116
- read_true(ParseInfo pi) {
117
+ static void read_true(ParseInfo pi) {
117
118
  if ('r' == *pi->cur++ && 'u' == *pi->cur++ && 'e' == *pi->cur++) {
118
- add_value(pi, Qtrue);
119
+ add_value(pi, Qtrue);
119
120
  } else {
120
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
121
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
121
122
  }
122
123
  }
123
124
 
124
- static void
125
- read_false(ParseInfo pi) {
125
+ static void read_false(ParseInfo pi) {
126
126
  if ('a' == *pi->cur++ && 'l' == *pi->cur++ && 's' == *pi->cur++ && 'e' == *pi->cur++) {
127
- add_value(pi, Qfalse);
127
+ add_value(pi, Qfalse);
128
128
  } else {
129
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
129
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
130
130
  }
131
131
  }
132
132
 
133
- static uint32_t
134
- read_hex(ParseInfo pi, const char *h) {
135
- uint32_t b = 0;
136
- int i;
133
+ static uint32_t read_hex(ParseInfo pi, const char *h) {
134
+ uint32_t b = 0;
135
+ int i;
137
136
 
138
137
  for (i = 0; i < 4; i++, h++) {
139
- b = b << 4;
140
- if ('0' <= *h && *h <= '9') {
141
- b += *h - '0';
142
- } else if ('A' <= *h && *h <= 'F') {
143
- b += *h - 'A' + 10;
144
- } else if ('a' <= *h && *h <= 'f') {
145
- b += *h - 'a' + 10;
146
- } else {
147
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
148
- return 0;
149
- }
138
+ b = b << 4;
139
+ if ('0' <= *h && *h <= '9') {
140
+ b += *h - '0';
141
+ } else if ('A' <= *h && *h <= 'F') {
142
+ b += *h - 'A' + 10;
143
+ } else if ('a' <= *h && *h <= 'f') {
144
+ b += *h - 'a' + 10;
145
+ } else {
146
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
147
+ return 0;
148
+ }
150
149
  }
151
150
  return b;
152
151
  }
153
152
 
154
- static void
155
- unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
153
+ static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
156
154
  if (0x0000007F >= code) {
157
- buf_append(buf, (char)code);
155
+ buf_append(buf, (char)code);
158
156
  } else if (0x000007FF >= code) {
159
- buf_append(buf, 0xC0 | (code >> 6));
160
- buf_append(buf, 0x80 | (0x3F & code));
157
+ buf_append(buf, 0xC0 | (code >> 6));
158
+ buf_append(buf, 0x80 | (0x3F & code));
161
159
  } else if (0x0000FFFF >= code) {
162
- buf_append(buf, 0xE0 | (code >> 12));
163
- buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
164
- buf_append(buf, 0x80 | (0x3F & code));
160
+ buf_append(buf, 0xE0 | (code >> 12));
161
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
162
+ buf_append(buf, 0x80 | (0x3F & code));
165
163
  } else if (0x001FFFFF >= code) {
166
- buf_append(buf, 0xF0 | (code >> 18));
167
- buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
168
- buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
169
- buf_append(buf, 0x80 | (0x3F & code));
164
+ buf_append(buf, 0xF0 | (code >> 18));
165
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
166
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
167
+ buf_append(buf, 0x80 | (0x3F & code));
170
168
  } else if (0x03FFFFFF >= code) {
171
- buf_append(buf, 0xF8 | (code >> 24));
172
- buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
173
- buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
174
- buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
175
- buf_append(buf, 0x80 | (0x3F & code));
169
+ buf_append(buf, 0xF8 | (code >> 24));
170
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
171
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
172
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
173
+ buf_append(buf, 0x80 | (0x3F & code));
176
174
  } else if (0x7FFFFFFF >= code) {
177
- buf_append(buf, 0xFC | (code >> 30));
178
- buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
179
- buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
180
- buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
181
- buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
182
- buf_append(buf, 0x80 | (0x3F & code));
175
+ buf_append(buf, 0xFC | (code >> 30));
176
+ buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
177
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
178
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
179
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
180
+ buf_append(buf, 0x80 | (0x3F & code));
183
181
  } else {
184
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
182
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
183
+ }
184
+ }
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
+ }
185
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, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
204
+ if (r != 16) {
205
+ str = (char*)(str + r);
206
+ return str;
207
+ }
208
+ }
209
+
210
+ return scan_string_noSIMD(str, end);
211
+ }
212
+ #endif
213
+
214
+ static const char *(*scan_func) (const char *str, const char *end) = scan_string_noSIMD;
215
+
216
+ void oj_scanner_init(void) {
217
+ #ifdef OJ_USE_SSE4_2
218
+ scan_func = scan_string_SIMD;
219
+ #endif
186
220
  }
187
221
 
188
222
  // entered at /
189
- static void
190
- read_escaped_str(ParseInfo pi, const char *start) {
191
- struct _Buf buf;
192
- const char *s;
193
- int cnt = (int)(pi->cur - start);
194
- uint32_t code;
195
- Val parent = stack_peek(&pi->stack);
223
+ static void read_escaped_str(ParseInfo pi, const char *start) {
224
+ struct _buf buf;
225
+ const char *s;
226
+ int cnt = (int)(pi->cur - start);
227
+ uint32_t code;
228
+ Val parent = stack_peek(&pi->stack);
196
229
 
197
230
  buf_init(&buf);
198
- if (0 < cnt) {
199
- buf_append_string(&buf, start, cnt);
200
- }
201
- for (s = pi->cur; '"' != *s; s++) {
202
- if (s >= pi->end) {
203
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
204
- buf_cleanup(&buf);
205
- return;
206
- } else if ('\\' == *s) {
207
- s++;
208
- switch (*s) {
209
- case 'n': buf_append(&buf, '\n'); break;
210
- case 'r': buf_append(&buf, '\r'); break;
211
- case 't': buf_append(&buf, '\t'); break;
212
- case 'f': buf_append(&buf, '\f'); break;
213
- case 'b': buf_append(&buf, '\b'); break;
214
- case '"': buf_append(&buf, '"'); break;
215
- case '/': buf_append(&buf, '/'); break;
216
- 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
- case 'u':
230
- s++;
231
- if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
232
- buf_cleanup(&buf);
233
- return;
234
- }
235
- s += 3;
236
- if (0x0000D800 <= code && code <= 0x0000DFFF) {
237
- uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
238
- uint32_t c2;
239
-
240
- s++;
241
- if ('\\' != *s || 'u' != *(s + 1)) {
242
- if (Yes == pi->options.allow_invalid) {
243
- s--;
244
- unicode_to_chars(pi, &buf, code);
245
- break;
246
- }
247
- pi->cur = s;
248
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
249
- buf_cleanup(&buf);
250
- return;
251
- }
252
- s += 2;
253
- if (0 == (c2 = read_hex(pi, s)) && err_has(&pi->err)) {
254
- buf_cleanup(&buf);
255
- return;
256
- }
257
- s += 3;
258
- c2 = (c2 - 0x0000DC00) & 0x000003FF;
259
- code = ((c1 << 10) | c2) + 0x00010000;
260
- }
261
- unicode_to_chars(pi, &buf, code);
262
- if (err_has(&pi->err)) {
263
- buf_cleanup(&buf);
264
- return;
265
- }
266
- break;
267
- default:
268
- pi->cur = s;
269
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
270
- buf_cleanup(&buf);
271
- return;
272
- }
273
- } else {
274
- buf_append(&buf, *s);
275
- }
231
+ buf_append_string(&buf, start, cnt);
232
+
233
+ for (s = pi->cur; '"' != *s;) {
234
+ const char *scanned = scan_func(s, pi->end);
235
+ if (scanned >= pi->end) {
236
+ oj_set_error_at(pi,
237
+ oj_parse_error_class,
238
+ __FILE__,
239
+ __LINE__,
240
+ "quoted string not terminated");
241
+ buf_cleanup(&buf);
242
+ return;
243
+ }
244
+
245
+ buf_append_string(&buf, s, (size_t)(scanned - s));
246
+ s = scanned;
247
+
248
+ if ('\\' == *s) {
249
+ s++;
250
+ switch (*s) {
251
+ case 'n': buf_append(&buf, '\n'); break;
252
+ case 'r': buf_append(&buf, '\r'); break;
253
+ case 't': buf_append(&buf, '\t'); break;
254
+ case 'f': buf_append(&buf, '\f'); break;
255
+ case 'b': buf_append(&buf, '\b'); break;
256
+ case '"': buf_append(&buf, '"'); break;
257
+ case '/': buf_append(&buf, '/'); break;
258
+ case '\\': buf_append(&buf, '\\'); break;
259
+ case 'u':
260
+ s++;
261
+ if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
262
+ buf_cleanup(&buf);
263
+ return;
264
+ }
265
+ s += 3;
266
+ if (0x0000D800 <= code && code <= 0x0000DFFF) {
267
+ uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
268
+ uint32_t c2;
269
+
270
+ s++;
271
+ if ('\\' != *s || 'u' != *(s + 1)) {
272
+ if (Yes == pi->options.allow_invalid) {
273
+ s--;
274
+ unicode_to_chars(pi, &buf, code);
275
+ break;
276
+ }
277
+ pi->cur = s;
278
+ oj_set_error_at(pi,
279
+ oj_parse_error_class,
280
+ __FILE__,
281
+ __LINE__,
282
+ "invalid escaped character");
283
+ buf_cleanup(&buf);
284
+ return;
285
+ }
286
+ s += 2;
287
+ if (0 == (c2 = read_hex(pi, s)) && err_has(&pi->err)) {
288
+ buf_cleanup(&buf);
289
+ return;
290
+ }
291
+ s += 3;
292
+ c2 = (c2 - 0x0000DC00) & 0x000003FF;
293
+ code = ((c1 << 10) | c2) + 0x00010000;
294
+ }
295
+ unicode_to_chars(pi, &buf, code);
296
+ if (err_has(&pi->err)) {
297
+ buf_cleanup(&buf);
298
+ return;
299
+ }
300
+ break;
301
+ default:
302
+ // The json gem claims this is not an error despite the
303
+ // ECMA-404 indicating it is not valid.
304
+ if (CompatMode == pi->options.mode) {
305
+ buf_append(&buf, *s);
306
+ break;
307
+ }
308
+ pi->cur = s;
309
+ oj_set_error_at(pi,
310
+ oj_parse_error_class,
311
+ __FILE__,
312
+ __LINE__,
313
+ "invalid escaped character");
314
+ buf_cleanup(&buf);
315
+ return;
316
+ }
317
+ s++;
318
+ }
276
319
  }
277
320
  if (0 == parent) {
278
- pi->add_cstr(pi, buf.head, buf_len(&buf), start);
321
+ pi->add_cstr(pi, buf.head, buf_len(&buf), start);
279
322
  } else {
280
- switch (parent->next) {
281
- case NEXT_ARRAY_NEW:
282
- case NEXT_ARRAY_ELEMENT:
283
- pi->array_append_cstr(pi, buf.head, buf_len(&buf), start);
284
- parent->next = NEXT_ARRAY_COMMA;
285
- break;
286
- case NEXT_HASH_NEW:
287
- case NEXT_HASH_KEY:
288
- if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
289
- parent->klen = buf_len(&buf);
290
- parent->key = malloc(parent->klen + 1);
291
- memcpy((char*)parent->key, buf.head, parent->klen);
292
- *(char*)(parent->key + parent->klen) = '\0';
293
- } else {
294
- parent->key = "";
295
- parent->klen = 0;
296
- }
297
- parent->k1 = *start;
298
- parent->next = NEXT_HASH_COLON;
299
- break;
300
- case NEXT_HASH_VALUE:
301
- pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), start);
302
- if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
303
- xfree((char*)parent->key);
304
- parent->key = 0;
305
- }
306
- parent->next = NEXT_HASH_COMMA;
307
- break;
308
- case NEXT_HASH_COMMA:
309
- case NEXT_NONE:
310
- case NEXT_ARRAY_COMMA:
311
- case NEXT_HASH_COLON:
312
- default:
313
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
314
- break;
315
- }
323
+ switch (parent->next) {
324
+ case NEXT_ARRAY_NEW:
325
+ case NEXT_ARRAY_ELEMENT:
326
+ pi->array_append_cstr(pi, buf.head, buf_len(&buf), start);
327
+ parent->next = NEXT_ARRAY_COMMA;
328
+ break;
329
+ case NEXT_HASH_NEW:
330
+ case NEXT_HASH_KEY:
331
+ if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
332
+ parent->klen = buf_len(&buf);
333
+ parent->key = malloc(parent->klen + 1);
334
+ memcpy((char *)parent->key, buf.head, parent->klen);
335
+ *(char *)(parent->key + parent->klen) = '\0';
336
+ } else {
337
+ parent->key = "";
338
+ parent->klen = 0;
339
+ }
340
+ parent->k1 = *start;
341
+ parent->next = NEXT_HASH_COLON;
342
+ break;
343
+ case NEXT_HASH_VALUE:
344
+ pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), start);
345
+ if (0 != parent->key && 0 < parent->klen &&
346
+ (parent->key < pi->json || pi->cur < parent->key)) {
347
+ xfree((char *)parent->key);
348
+ parent->key = 0;
349
+ }
350
+ parent->next = NEXT_HASH_COMMA;
351
+ break;
352
+ case NEXT_HASH_COMMA:
353
+ case NEXT_NONE:
354
+ case NEXT_ARRAY_COMMA:
355
+ case NEXT_HASH_COLON:
356
+ default:
357
+ oj_set_error_at(pi,
358
+ oj_parse_error_class,
359
+ __FILE__,
360
+ __LINE__,
361
+ "expected %s, not a string",
362
+ oj_stack_next_string(parent->next));
363
+ break;
364
+ }
316
365
  }
317
366
  pi->cur = s + 1;
318
367
  buf_cleanup(&buf);
319
368
  }
320
369
 
321
- static void
322
- read_str(ParseInfo pi) {
323
- const char *str = pi->cur;
324
- Val parent = stack_peek(&pi->stack);
325
-
326
- for (; '"' != *pi->cur; pi->cur++) {
327
- if (pi->end <= pi->cur) {
328
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
329
- return;
330
- } else if ('\0' == *pi->cur) {
331
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
332
- return;
333
- } else if ('\\' == *pi->cur) {
334
- read_escaped_str(pi, str);
335
- return;
336
- }
370
+ static void read_str(ParseInfo pi) {
371
+ const char *str = pi->cur;
372
+ Val parent = stack_peek(&pi->stack);
373
+
374
+ pi->cur = scan_func(pi->cur, pi->end);
375
+ if (RB_UNLIKELY(pi->end <= pi->cur)) {
376
+ oj_set_error_at(pi,
377
+ oj_parse_error_class,
378
+ __FILE__,
379
+ __LINE__,
380
+ "quoted string not terminated");
381
+ return;
382
+ }
383
+ if (RB_UNLIKELY('\0' == *pi->cur)) {
384
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
385
+ return;
337
386
  }
338
- if (0 == parent) { // simple add
339
- pi->add_cstr(pi, str, pi->cur - str, str);
387
+ if ('\\' == *pi->cur) {
388
+ read_escaped_str(pi, str);
389
+ return;
390
+ }
391
+
392
+ if (0 == parent) { // simple add
393
+ pi->add_cstr(pi, str, pi->cur - str, str);
340
394
  } else {
341
- switch (parent->next) {
342
- case NEXT_ARRAY_NEW:
343
- case NEXT_ARRAY_ELEMENT:
344
- pi->array_append_cstr(pi, str, pi->cur - str, str);
345
- parent->next = NEXT_ARRAY_COMMA;
346
- break;
347
- case NEXT_HASH_NEW:
348
- case NEXT_HASH_KEY:
349
- if (Qundef == (parent->key_val = pi->hash_key(pi, str, pi->cur - str))) {
350
- parent->key = str;
351
- parent->klen = pi->cur - str;
352
- } else {
353
- parent->key = "";
354
- parent->klen = 0;
355
- }
356
- parent->k1 = *str;
357
- parent->next = NEXT_HASH_COLON;
358
- break;
359
- case NEXT_HASH_VALUE:
360
- pi->hash_set_cstr(pi, parent, str, pi->cur - str, str);
361
- if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
362
- xfree((char*)parent->key);
363
- parent->key = 0;
364
- }
365
- parent->next = NEXT_HASH_COMMA;
366
- break;
367
- case NEXT_HASH_COMMA:
368
- case NEXT_NONE:
369
- case NEXT_ARRAY_COMMA:
370
- case NEXT_HASH_COLON:
371
- default:
372
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
373
- break;
374
- }
395
+ switch (parent->next) {
396
+ case NEXT_ARRAY_NEW:
397
+ case NEXT_ARRAY_ELEMENT:
398
+ pi->array_append_cstr(pi, str, pi->cur - str, str);
399
+ parent->next = NEXT_ARRAY_COMMA;
400
+ break;
401
+ case NEXT_HASH_NEW:
402
+ case NEXT_HASH_KEY:
403
+ if (Qundef == (parent->key_val = pi->hash_key(pi, str, pi->cur - str))) {
404
+ parent->key = str;
405
+ parent->klen = pi->cur - str;
406
+ } else {
407
+ parent->key = "";
408
+ parent->klen = 0;
409
+ }
410
+ parent->k1 = *str;
411
+ parent->next = NEXT_HASH_COLON;
412
+ break;
413
+ case NEXT_HASH_VALUE:
414
+ pi->hash_set_cstr(pi, parent, str, pi->cur - str, str);
415
+ if (0 != parent->key && 0 < parent->klen &&
416
+ (parent->key < pi->json || pi->cur < parent->key)) {
417
+ xfree((char *)parent->key);
418
+ parent->key = 0;
419
+ }
420
+ parent->next = NEXT_HASH_COMMA;
421
+ break;
422
+ case NEXT_HASH_COMMA:
423
+ case NEXT_NONE:
424
+ case NEXT_ARRAY_COMMA:
425
+ case NEXT_HASH_COLON:
426
+ default:
427
+ oj_set_error_at(pi,
428
+ oj_parse_error_class,
429
+ __FILE__,
430
+ __LINE__,
431
+ "expected %s, not a string",
432
+ oj_stack_next_string(parent->next));
433
+ break;
434
+ }
375
435
  }
376
- pi->cur++; // move past "
436
+ pi->cur++; // move past "
377
437
  }
378
438
 
379
- static void
380
- read_num(ParseInfo pi) {
381
- struct _NumInfo ni;
382
- Val parent = stack_peek(&pi->stack);
383
-
384
- ni.str = pi->cur;
385
- ni.i = 0;
386
- ni.num = 0;
387
- ni.div = 1;
388
- ni.di = 0;
389
- ni.len = 0;
390
- ni.exp = 0;
391
- ni.big = 0;
439
+ static void read_num(ParseInfo pi) {
440
+ struct _numInfo ni;
441
+ Val parent = stack_peek(&pi->stack);
442
+
443
+ ni.str = pi->cur;
444
+ ni.i = 0;
445
+ ni.num = 0;
446
+ ni.div = 1;
447
+ ni.di = 0;
448
+ ni.len = 0;
449
+ ni.exp = 0;
450
+ ni.big = 0;
392
451
  ni.infinity = 0;
393
- ni.nan = 0;
394
- ni.neg = 0;
395
- ni.hasExp = 0;
396
- ni.no_big = (FloatDec == pi->options.bigdec_load);
452
+ ni.nan = 0;
453
+ ni.neg = 0;
454
+ ni.has_exp = 0;
455
+ if (CompatMode == pi->options.mode) {
456
+ ni.no_big = !pi->options.compat_bigdec;
457
+ ni.bigdec_load = pi->options.compat_bigdec;
458
+ } else {
459
+ ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
460
+ RubyDec == pi->options.bigdec_load);
461
+ ni.bigdec_load = pi->options.bigdec_load;
462
+ }
397
463
 
398
464
  if ('-' == *pi->cur) {
399
- pi->cur++;
400
- ni.neg = 1;
465
+ pi->cur++;
466
+ ni.neg = 1;
401
467
  } else if ('+' == *pi->cur) {
402
- pi->cur++;
468
+ if (StrictMode == pi->options.mode) {
469
+ oj_set_error_at(pi,
470
+ oj_parse_error_class,
471
+ __FILE__,
472
+ __LINE__,
473
+ "not a number or other value");
474
+ return;
475
+ }
476
+ pi->cur++;
403
477
  }
404
478
  if ('I' == *pi->cur) {
405
- if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
406
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
407
- return;
408
- }
409
- pi->cur += 8;
410
- ni.infinity = 1;
479
+ if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
480
+ oj_set_error_at(pi,
481
+ oj_parse_error_class,
482
+ __FILE__,
483
+ __LINE__,
484
+ "not a number or other value");
485
+ return;
486
+ }
487
+ pi->cur += 8;
488
+ ni.infinity = 1;
411
489
  } else if ('N' == *pi->cur || 'n' == *pi->cur) {
412
- if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
413
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
414
- return;
415
- }
416
- pi->cur += 3;
417
- ni.nan = 1;
490
+ if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
491
+ oj_set_error_at(pi,
492
+ oj_parse_error_class,
493
+ __FILE__,
494
+ __LINE__,
495
+ "not a number or other value");
496
+ return;
497
+ }
498
+ pi->cur += 3;
499
+ ni.nan = 1;
418
500
  } else {
419
- int dec_cnt = 0;
420
- bool zero1 = false;
421
-
422
- for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
423
- if (0 == ni.i && '0' == *pi->cur) {
424
- zero1 = true;
425
- }
426
- if (0 < ni.i) {
427
- dec_cnt++;
428
- }
429
- if (!ni.big) {
430
- int d = (*pi->cur - '0');
431
-
432
- if (0 < d) {
433
- if (zero1 && CompatMode == pi->options.mode) {
434
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
435
- return;
436
- }
437
- zero1 = false;
438
- }
439
- ni.i = ni.i * 10 + d;
440
- if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
441
- ni.big = 1;
442
- }
443
- }
444
- }
445
- if ('.' == *pi->cur) {
446
- pi->cur++;
447
- // 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
- if (*pi->cur < '0' || '9' < *pi->cur) {
451
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
452
- return;
453
- }
454
- }
455
- for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
456
- int d = (*pi->cur - '0');
457
-
458
- if (0 < ni.num || 0 < ni.i) {
459
- dec_cnt++;
460
- }
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;
466
- }
467
- }
468
- }
469
- if ('e' == *pi->cur || 'E' == *pi->cur) {
470
- int eneg = 0;
471
-
472
- ni.hasExp = 1;
473
- pi->cur++;
474
- if ('-' == *pi->cur) {
475
- pi->cur++;
476
- eneg = 1;
477
- } else if ('+' == *pi->cur) {
478
- pi->cur++;
479
- }
480
- for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
481
- ni.exp = ni.exp * 10 + (*pi->cur - '0');
482
- if (EXP_MAX <= ni.exp) {
483
- ni.big = 1;
484
- }
485
- }
486
- if (eneg) {
487
- ni.exp = -ni.exp;
488
- }
489
- }
490
- ni.len = pi->cur - ni.str;
501
+ int dec_cnt = 0;
502
+ bool zero1 = false;
503
+
504
+ // Skip leading zeros.
505
+ for (; '0' == *pi->cur; pi->cur++) {
506
+ zero1 = true;
507
+ }
508
+
509
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
510
+ int d = (*pi->cur - '0');
511
+
512
+ if (RB_LIKELY(0 != ni.i)) {
513
+ dec_cnt++;
514
+ }
515
+ ni.i = ni.i * 10 + d;
516
+ }
517
+ if (RB_UNLIKELY(0 != ni.i && zero1 && CompatMode == pi->options.mode)) {
518
+ oj_set_error_at(pi,
519
+ oj_parse_error_class,
520
+ __FILE__,
521
+ __LINE__,
522
+ "not a number");
523
+ return;
524
+ }
525
+ if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
526
+ ni.big = true;
527
+ }
528
+
529
+ if ('.' == *pi->cur) {
530
+ pi->cur++;
531
+ // A trailing . is not a valid decimal but if encountered allow it
532
+ // except when mimicking the JSON gem or in strict mode.
533
+ if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
534
+ int pos = (int)(pi->cur - ni.str);
535
+
536
+ if (1 == pos || (2 == pos && ni.neg)) {
537
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
538
+ return;
539
+ }
540
+ if (*pi->cur < '0' || '9' < *pi->cur) {
541
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
542
+ return;
543
+ }
544
+ }
545
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
546
+ int d = (*pi->cur - '0');
547
+
548
+ if (RB_LIKELY(0 != ni.num || 0 != ni.i)) {
549
+ dec_cnt++;
550
+ }
551
+ ni.num = ni.num * 10 + d;
552
+ ni.div *= 10;
553
+ ni.di++;
554
+ }
555
+ }
556
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
557
+ if (!ni.no_big) {
558
+ ni.big = true;
559
+ }
560
+ }
561
+
562
+ if ('e' == *pi->cur || 'E' == *pi->cur) {
563
+ int eneg = 0;
564
+
565
+ ni.has_exp = 1;
566
+ pi->cur++;
567
+ if ('-' == *pi->cur) {
568
+ pi->cur++;
569
+ eneg = 1;
570
+ } else if ('+' == *pi->cur) {
571
+ pi->cur++;
572
+ }
573
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
574
+ ni.exp = ni.exp * 10 + (*pi->cur - '0');
575
+ if (EXP_MAX <= ni.exp) {
576
+ ni.big = true;
577
+ }
578
+ }
579
+ if (eneg) {
580
+ ni.exp = -ni.exp;
581
+ }
582
+ }
583
+ ni.len = pi->cur - ni.str;
491
584
  }
492
585
  // Check for special reserved values for Infinity and NaN.
493
586
  if (ni.big) {
494
- if (0 == strcasecmp(INF_VAL, ni.str)) {
495
- ni.infinity = 1;
496
- } else if (0 == strcasecmp(NINF_VAL, ni.str)) {
497
- ni.infinity = 1;
498
- ni.neg = 1;
499
- } else if (0 == strcasecmp(NAN_VAL, ni.str)) {
500
- ni.nan = 1;
501
- }
587
+ if (0 == strcasecmp(INF_VAL, ni.str)) {
588
+ ni.infinity = 1;
589
+ } else if (0 == strcasecmp(NINF_VAL, ni.str)) {
590
+ ni.infinity = 1;
591
+ ni.neg = 1;
592
+ } else if (0 == strcasecmp(NAN_VAL, ni.str)) {
593
+ ni.nan = 1;
594
+ }
502
595
  }
503
- if (BigDec == pi->options.bigdec_load) {
504
- ni.big = 1;
596
+ if (CompatMode == pi->options.mode) {
597
+ if (pi->options.compat_bigdec) {
598
+ ni.big = 1;
599
+ }
600
+ } else if (BigDec == pi->options.bigdec_load) {
601
+ ni.big = 1;
505
602
  }
506
603
  if (0 == parent) {
507
- pi->add_num(pi, &ni);
604
+ pi->add_num(pi, &ni);
508
605
  } else {
509
- switch (parent->next) {
510
- case NEXT_ARRAY_NEW:
511
- case NEXT_ARRAY_ELEMENT:
512
- pi->array_append_num(pi, &ni);
513
- parent->next = NEXT_ARRAY_COMMA;
514
- break;
515
- case NEXT_HASH_VALUE:
516
- pi->hash_set_num(pi, parent, &ni);
517
- if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
518
- xfree((char*)parent->key);
519
- parent->key = 0;
520
- }
521
- parent->next = NEXT_HASH_COMMA;
522
- break;
523
- default:
524
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
525
- break;
526
- }
606
+ switch (parent->next) {
607
+ case NEXT_ARRAY_NEW:
608
+ case NEXT_ARRAY_ELEMENT:
609
+ pi->array_append_num(pi, &ni);
610
+ parent->next = NEXT_ARRAY_COMMA;
611
+ break;
612
+ case NEXT_HASH_VALUE:
613
+ pi->hash_set_num(pi, parent, &ni);
614
+ if (0 != parent->key && 0 < parent->klen &&
615
+ (parent->key < pi->json || pi->cur < parent->key)) {
616
+ xfree((char *)parent->key);
617
+ parent->key = 0;
618
+ }
619
+ parent->next = NEXT_HASH_COMMA;
620
+ break;
621
+ default:
622
+ oj_set_error_at(pi,
623
+ oj_parse_error_class,
624
+ __FILE__,
625
+ __LINE__,
626
+ "expected %s",
627
+ oj_stack_next_string(parent->next));
628
+ break;
629
+ }
527
630
  }
528
631
  }
529
632
 
530
- static void
531
- array_start(ParseInfo pi) {
532
- volatile VALUE v = pi->start_array(pi);
633
+ static void array_start(ParseInfo pi) {
634
+ VALUE v = pi->start_array(pi);
533
635
 
534
636
  stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
535
637
  }
536
638
 
537
- static void
538
- array_end(ParseInfo pi) {
539
- Val array = stack_pop(&pi->stack);
639
+ static void array_end(ParseInfo pi) {
640
+ Val array = stack_pop(&pi->stack);
540
641
 
541
642
  if (0 == array) {
542
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
643
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
543
644
  } else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
544
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not an array close", oj_stack_next_string(array->next));
645
+ oj_set_error_at(pi,
646
+ oj_parse_error_class,
647
+ __FILE__,
648
+ __LINE__,
649
+ "expected %s, not an array close",
650
+ oj_stack_next_string(array->next));
545
651
  } else {
546
- pi->end_array(pi);
547
- add_value(pi, array->val);
652
+ pi->end_array(pi);
653
+ add_value(pi, array->val);
548
654
  }
549
655
  }
550
656
 
551
- static void
552
- hash_start(ParseInfo pi) {
553
- volatile VALUE v = pi->start_hash(pi);
657
+ static void hash_start(ParseInfo pi) {
658
+ VALUE v = pi->start_hash(pi);
554
659
 
555
660
  stack_push(&pi->stack, v, NEXT_HASH_NEW);
556
661
  }
557
662
 
558
- static void
559
- hash_end(ParseInfo pi) {
560
- volatile Val hash = stack_peek(&pi->stack);
663
+ static void hash_end(ParseInfo pi) {
664
+ Val hash = stack_peek(&pi->stack);
561
665
 
562
666
  // leave hash on stack until just before
563
667
  if (0 == hash) {
564
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
668
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
565
669
  } else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
566
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a hash close", oj_stack_next_string(hash->next));
670
+ oj_set_error_at(pi,
671
+ oj_parse_error_class,
672
+ __FILE__,
673
+ __LINE__,
674
+ "expected %s, not a hash close",
675
+ oj_stack_next_string(hash->next));
567
676
  } else {
568
- pi->end_hash(pi);
569
- stack_pop(&pi->stack);
570
- add_value(pi, hash->val);
677
+ pi->end_hash(pi);
678
+ stack_pop(&pi->stack);
679
+ add_value(pi, hash->val);
571
680
  }
572
681
  }
573
682
 
574
- static void
575
- comma(ParseInfo pi) {
576
- Val parent = stack_peek(&pi->stack);
683
+ static void comma(ParseInfo pi) {
684
+ Val parent = stack_peek(&pi->stack);
577
685
 
578
686
  if (0 == parent) {
579
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
687
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
580
688
  } else if (NEXT_ARRAY_COMMA == parent->next) {
581
- parent->next = NEXT_ARRAY_ELEMENT;
689
+ parent->next = NEXT_ARRAY_ELEMENT;
582
690
  } else if (NEXT_HASH_COMMA == parent->next) {
583
- parent->next = NEXT_HASH_KEY;
691
+ parent->next = NEXT_HASH_KEY;
584
692
  } else {
585
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
693
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
586
694
  }
587
695
  }
588
696
 
589
- static void
590
- colon(ParseInfo pi) {
591
- Val parent = stack_peek(&pi->stack);
697
+ static void colon(ParseInfo pi) {
698
+ Val parent = stack_peek(&pi->stack);
592
699
 
593
700
  if (0 != parent && NEXT_HASH_COLON == parent->next) {
594
- parent->next = NEXT_HASH_VALUE;
701
+ parent->next = NEXT_HASH_VALUE;
595
702
  } else {
596
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
703
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
597
704
  }
598
705
  }
599
706
 
600
- void
601
- oj_parse2(ParseInfo pi) {
602
- int first = 1;
603
- long start = 0;
707
+ void oj_parse2(ParseInfo pi) {
708
+ int first = 1;
709
+ long start = 0;
604
710
 
605
711
  pi->cur = pi->json;
606
712
  err_init(&pi->err);
607
713
  while (1) {
608
- if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
609
- VALUE err_clas = oj_get_json_err_class("NestingError");
610
-
611
- oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
612
- pi->err_class = err_clas;
613
- return;
614
- }
615
- next_non_white(pi);
616
- if (!first && '\0' != *pi->cur) {
617
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
618
- }
714
+ if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
715
+ VALUE err_clas = oj_get_json_err_class("NestingError");
619
716
 
620
- // If no tokens are consumed (i.e. empty string), throw a parse error
621
- // this is the behavior of JSON.parse in both Ruby and JS.
622
- if (No == pi->options.empty_string && 1 == first && '\0' == *pi->cur) {
623
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
624
- }
717
+ oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
718
+ pi->err_class = err_clas;
719
+ return;
720
+ }
721
+ next_non_white(pi);
722
+ if (!first && '\0' != *pi->cur) {
723
+ oj_set_error_at(pi,
724
+ oj_parse_error_class,
725
+ __FILE__,
726
+ __LINE__,
727
+ "unexpected characters after the JSON document");
728
+ }
625
729
 
626
- switch (*pi->cur++) {
627
- case '{':
628
- hash_start(pi);
629
- break;
630
- case '}':
631
- hash_end(pi);
632
- break;
633
- case ':':
634
- colon(pi);
635
- break;
636
- case '[':
637
- array_start(pi);
638
- break;
639
- case ']':
640
- array_end(pi);
641
- break;
642
- case ',':
643
- comma(pi);
644
- break;
645
- case '"':
646
- read_str(pi);
647
- break;
648
- //case '+':
649
- case '+':
650
- if (CompatMode == pi->options.mode) {
651
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
652
- return;
653
- }
654
- pi->cur--;
655
- read_num(pi);
656
- break;
657
- case '-':
658
- case '0':
659
- case '1':
660
- case '2':
661
- case '3':
662
- case '4':
663
- case '5':
664
- case '6':
665
- case '7':
666
- case '8':
667
- case '9':
668
- pi->cur--;
669
- read_num(pi);
670
- break;
671
- case 'I':
672
- case 'N':
673
- if (Yes == pi->options.allow_nan) {
674
- pi->cur--;
675
- read_num(pi);
676
- } else {
677
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
678
- }
679
- break;
680
- case 't':
681
- read_true(pi);
682
- break;
683
- case 'f':
684
- read_false(pi);
685
- break;
686
- case 'n':
687
- if ('u' == *pi->cur) {
688
- read_null(pi);
689
- } else {
690
- pi->cur--;
691
- read_num(pi);
692
- }
693
- break;
694
- case '/':
695
- skip_comment(pi);
696
- if (first) {
697
- continue;
698
- }
699
- break;
700
- case '\0':
701
- pi->cur--;
702
- return;
703
- default:
704
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
705
- return;
706
- }
707
- if (err_has(&pi->err)) {
708
- return;
709
- }
710
- if (stack_empty(&pi->stack)) {
711
- if (Qundef != pi->proc) {
712
- VALUE args[3];
713
- long len = (pi->cur - pi->json) - start;
714
-
715
- *args = stack_head_val(&pi->stack);
716
- args[1] = LONG2NUM(start);
717
- args[2] = LONG2NUM(len);
718
-
719
- if (Qnil == pi->proc) {
720
- rb_yield_values2(3, args);
721
- } else {
722
- rb_proc_call_with_block(pi->proc, 3, args, Qnil);
723
- }
724
- } else if (!pi->has_callbacks) {
725
- first = 0;
726
- }
727
- start = pi->cur - pi->json;
728
- }
730
+ // If no tokens are consumed (i.e. empty string), throw a parse error
731
+ // this is the behavior of JSON.parse in both Ruby and JS.
732
+ if (No == pi->options.empty_string && 1 == first && '\0' == *pi->cur) {
733
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
734
+ }
735
+
736
+ switch (*pi->cur++) {
737
+ case '{': hash_start(pi); break;
738
+ case '}': hash_end(pi); break;
739
+ case ':': colon(pi); break;
740
+ case '[': array_start(pi); break;
741
+ case ']': array_end(pi); break;
742
+ case ',': comma(pi); break;
743
+ case '"':
744
+ read_str(pi);
745
+ break;
746
+ // case '+':
747
+ case '+':
748
+ if (CompatMode == pi->options.mode) {
749
+ oj_set_error_at(pi,
750
+ oj_parse_error_class,
751
+ __FILE__,
752
+ __LINE__,
753
+ "unexpected character");
754
+ return;
755
+ }
756
+ pi->cur--;
757
+ read_num(pi);
758
+ break;
759
+ case '-':
760
+ case '0':
761
+ case '1':
762
+ case '2':
763
+ case '3':
764
+ case '4':
765
+ case '5':
766
+ case '6':
767
+ case '7':
768
+ case '8':
769
+ case '9':
770
+ pi->cur--;
771
+ read_num(pi);
772
+ break;
773
+ case 'I':
774
+ case 'N':
775
+ if (Yes == pi->options.allow_nan) {
776
+ pi->cur--;
777
+ read_num(pi);
778
+ } else {
779
+ oj_set_error_at(pi,
780
+ oj_parse_error_class,
781
+ __FILE__,
782
+ __LINE__,
783
+ "unexpected character");
784
+ }
785
+ break;
786
+ case 't': read_true(pi); break;
787
+ case 'f': read_false(pi); break;
788
+ case 'n':
789
+ if ('u' == *pi->cur) {
790
+ read_null(pi);
791
+ } else {
792
+ pi->cur--;
793
+ read_num(pi);
794
+ }
795
+ break;
796
+ case '/':
797
+ skip_comment(pi);
798
+ if (first) {
799
+ continue;
800
+ }
801
+ break;
802
+ case '\0': pi->cur--; return;
803
+ default:
804
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
805
+ return;
806
+ }
807
+ if (err_has(&pi->err)) {
808
+ return;
809
+ }
810
+ if (stack_empty(&pi->stack)) {
811
+ if (Qundef != pi->proc) {
812
+ VALUE args[3];
813
+ long len = (pi->cur - pi->json) - start;
814
+
815
+ *args = stack_head_val(&pi->stack);
816
+ args[1] = LONG2NUM(start);
817
+ args[2] = LONG2NUM(len);
818
+
819
+ if (Qnil == pi->proc) {
820
+ rb_yield_values2(3, args);
821
+ } else {
822
+ rb_proc_call_with_block(pi->proc, 3, args, Qnil);
823
+ }
824
+ } else if (!pi->has_callbacks) {
825
+ first = 0;
826
+ }
827
+ start = pi->cur - pi->json;
828
+ }
729
829
  }
730
830
  }
731
831
 
832
+ static VALUE rescue_big_decimal(VALUE str, VALUE ignore) {
833
+ rb_raise(oj_parse_error_class, "Invalid value for BigDecimal()");
834
+ return Qnil;
835
+ }
836
+
837
+ static VALUE parse_big_decimal(VALUE str) {
838
+ return rb_funcall(rb_cObject, oj_bigdecimal_id, 1, str);
839
+ }
840
+
841
+ static long double exp_plus[] = {
842
+ 1.0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
843
+ 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19,
844
+ 1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29,
845
+ 1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, 1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39,
846
+ 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
847
+ };
848
+
732
849
  VALUE
733
850
  oj_num_as_value(NumInfo ni) {
734
- volatile VALUE rnum = Qnil;
851
+ VALUE rnum = Qnil;
735
852
 
736
853
  if (ni->infinity) {
737
- if (ni->neg) {
738
- rnum = rb_float_new(-OJ_INFINITY);
739
- } else {
740
- rnum = rb_float_new(OJ_INFINITY);
741
- }
854
+ if (ni->neg) {
855
+ rnum = rb_float_new(-OJ_INFINITY);
856
+ } else {
857
+ rnum = rb_float_new(OJ_INFINITY);
858
+ }
742
859
  } else if (ni->nan) {
743
- rnum = rb_float_new(0.0/0.0);
744
- } else if (1 == ni->div && 0 == ni->exp) { // fixnum
745
- if (ni->big) {
746
- if (256 > ni->len) {
747
- char buf[256];
748
-
749
- memcpy(buf, ni->str, ni->len);
750
- buf[ni->len] = '\0';
751
- rnum = rb_cstr_to_inum(buf, 10, 0);
752
- } else {
753
- char *buf = ALLOC_N(char, ni->len + 1);
754
-
755
- memcpy(buf, ni->str, ni->len);
756
- buf[ni->len] = '\0';
757
- rnum = rb_cstr_to_inum(buf, 10, 0);
758
- xfree(buf);
759
- }
760
- } else {
761
- if (ni->neg) {
762
- rnum = rb_ll2inum(-ni->i);
763
- } else {
764
- rnum = rb_ll2inum(ni->i);
765
- }
766
- }
767
- } else { // decimal
768
- if (ni->big) {
769
- rnum = rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(ni->str, ni->len));
770
- if (ni->no_big) {
771
- rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
772
- }
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;
776
- int x = (int)((int64_t)ni->exp - ni->di);
777
-
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);
792
- }
793
- if (ni->neg) {
794
- d = -d;
795
- }
796
- rnum = rb_float_new((double)d);
797
- }
798
- }
860
+ rnum = rb_float_new(0.0 / 0.0);
861
+ } else if (1 == ni->div && 0 == ni->exp && !ni->has_exp) { // fixnum
862
+ if (ni->big) {
863
+ if (256 > ni->len) {
864
+ char buf[256];
865
+
866
+ memcpy(buf, ni->str, ni->len);
867
+ buf[ni->len] = '\0';
868
+ rnum = rb_cstr_to_inum(buf, 10, 0);
869
+ } else {
870
+ char *buf = ALLOC_N(char, ni->len + 1);
871
+
872
+ memcpy(buf, ni->str, ni->len);
873
+ buf[ni->len] = '\0';
874
+ rnum = rb_cstr_to_inum(buf, 10, 0);
875
+ xfree(buf);
876
+ }
877
+ } else {
878
+ if (ni->neg) {
879
+ rnum = rb_ll2inum(-ni->i);
880
+ } else {
881
+ rnum = rb_ll2inum(ni->i);
882
+ }
883
+ }
884
+ } else { // decimal
885
+ if (ni->big) {
886
+ VALUE bd = rb_str_new(ni->str, ni->len);
887
+
888
+ rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
889
+ if (ni->no_big) {
890
+ rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
891
+ }
892
+ } else if (FastDec == ni->bigdec_load) {
893
+ long double ld = (long double)ni->i * (long double)ni->div + (long double)ni->num;
894
+ int x = (int)((int64_t)ni->exp - ni->di);
895
+
896
+ if (0 < x) {
897
+ if (x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
898
+ ld *= exp_plus[x];
899
+ } else {
900
+ ld *= powl(10.0, x);
901
+ }
902
+ } else if (x < 0) {
903
+ if (-x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
904
+ ld /= exp_plus[-x];
905
+ } else {
906
+ ld /= powl(10.0, -x);
907
+ }
908
+ }
909
+ if (ni->neg) {
910
+ ld = -ld;
911
+ }
912
+ rnum = rb_float_new((double)ld);
913
+ } else if (RubyDec == ni->bigdec_load) {
914
+ VALUE sv = rb_str_new(ni->str, ni->len);
915
+
916
+ rnum = rb_funcall(sv, rb_intern("to_f"), 0);
917
+ } else {
918
+ char * end;
919
+ double d = strtod(ni->str, &end);
920
+
921
+ if ((long)ni->len != (long)(end - ni->str)) {
922
+ rb_raise(oj_parse_error_class, "Invalid float");
923
+ }
924
+ rnum = rb_float_new(d);
925
+ }
799
926
  }
800
927
  return rnum;
801
928
  }
802
929
 
803
- void
804
- oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const char *format, ...) {
805
- va_list ap;
806
- char msg[256];
807
- char *p = msg;
808
- char *end = p + sizeof(msg) - 2;
809
- char *start;
810
- Val vp;
930
+ void oj_set_error_at(ParseInfo pi,
931
+ VALUE err_clas,
932
+ const char *file,
933
+ int line,
934
+ const char *format,
935
+ ...) {
936
+ va_list ap;
937
+ char msg[256];
938
+ char * p = msg;
939
+ char * end = p + sizeof(msg) - 2;
940
+ char * start;
941
+ Val vp;
942
+ int mlen;
811
943
 
812
944
  va_start(ap, format);
813
- p += vsnprintf(msg, sizeof(msg) - 1, format, ap);
945
+ mlen = vsnprintf(msg, sizeof(msg) - 1, format, ap);
946
+ if (0 < mlen) {
947
+ if (sizeof(msg) - 2 < (size_t)mlen) {
948
+ p = end - 2;
949
+ } else {
950
+ p += mlen;
951
+ }
952
+ }
814
953
  va_end(ap);
815
954
  pi->err.clas = err_clas;
816
955
  if (p + 3 < end) {
817
- *p++ = ' ';
818
- *p++ = '(';
819
- start = p;
820
- for (vp = pi->stack.head; vp < pi->stack.tail; vp++) {
821
- if (end <= p + 1 + vp->klen) {
822
- break;
823
- }
824
- if (NULL != vp->key) {
825
- if (start < p) {
826
- *p++ = '.';
827
- }
828
- memcpy(p, vp->key, vp->klen);
829
- p += vp->klen;
830
- } else {
831
- if (RUBY_T_ARRAY == rb_type(vp->val)) {
832
- if (end <= p + 12) {
833
- break;
834
- }
835
- p += snprintf(p, end - p, "[%ld]", RARRAY_LEN(vp->val));
836
- }
837
- }
838
- }
839
- *p++ = ')';
956
+ *p++ = ' ';
957
+ *p++ = '(';
958
+ *p++ = 'a';
959
+ *p++ = 'f';
960
+ *p++ = 't';
961
+ *p++ = 'e';
962
+ *p++ = 'r';
963
+ *p++ = ' ';
964
+ start = p;
965
+ for (vp = pi->stack.head; vp < pi->stack.tail; vp++) {
966
+ if (end <= p + 1 + vp->klen) {
967
+ break;
968
+ }
969
+ if (NULL != vp->key) {
970
+ if (start < p) {
971
+ *p++ = '.';
972
+ }
973
+ memcpy(p, vp->key, vp->klen);
974
+ p += vp->klen;
975
+ } else {
976
+ if (RUBY_T_ARRAY == rb_type(vp->val)) {
977
+ if (end <= p + 12) {
978
+ break;
979
+ }
980
+ p += snprintf(p, end - p, "[%ld]", RARRAY_LEN(vp->val));
981
+ }
982
+ }
983
+ }
984
+ *p++ = ')';
840
985
  }
841
986
  *p = '\0';
842
987
  if (0 == pi->json) {
843
- oj_err_set(&pi->err, err_clas, "%s at line %d, column %d [%s:%d]", msg, pi->rd.line, pi->rd.col, file, line);
988
+ oj_err_set(&pi->err,
989
+ err_clas,
990
+ "%s at line %d, column %d [%s:%d]",
991
+ msg,
992
+ pi->rd.line,
993
+ pi->rd.col,
994
+ file,
995
+ line);
844
996
  } else {
845
- _oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
997
+ _oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
846
998
  }
847
999
  }
848
1000
 
849
- static VALUE
850
- protect_parse(VALUE pip) {
1001
+ static VALUE protect_parse(VALUE pip) {
851
1002
  oj_parse2((ParseInfo)pip);
852
1003
 
853
1004
  return Qnil;
@@ -855,102 +1006,103 @@ protect_parse(VALUE pip) {
855
1006
 
856
1007
  extern int oj_utf8_index;
857
1008
 
858
- static void
859
- oj_pi_set_input_str(ParseInfo pi, volatile VALUE *inputp) {
860
- rb_encoding *enc = rb_to_encoding(rb_obj_encoding(*inputp));
1009
+ static void oj_pi_set_input_str(ParseInfo pi, VALUE *inputp) {
1010
+ int idx = RB_ENCODING_GET(*inputp);
861
1011
 
862
- if (rb_utf8_encoding() != enc) {
863
- *inputp = rb_str_conv_enc(*inputp, enc, rb_utf8_encoding());
1012
+ if (oj_utf8_encoding_index != idx) {
1013
+ rb_encoding *enc = rb_enc_from_index(idx);
1014
+ *inputp = rb_str_conv_enc(*inputp, enc, oj_utf8_encoding);
864
1015
  }
865
- pi->json = rb_string_value_ptr((VALUE*)inputp);
866
- pi->end = pi->json + RSTRING_LEN(*inputp);
1016
+ pi->json = RSTRING_PTR(*inputp);
1017
+ pi->end = pi->json + RSTRING_LEN(*inputp);
867
1018
  }
868
1019
 
869
1020
  VALUE
870
1021
  oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
871
- char *buf = 0;
872
- volatile VALUE input;
873
- volatile VALUE wrapped_stack;
874
- volatile VALUE result = Qnil;
875
- int line = 0;
876
- int free_json = 0;
1022
+ char * buf = 0;
1023
+ VALUE input;
1024
+ VALUE wrapped_stack;
1025
+ VALUE result = Qnil;
1026
+ int line = 0;
1027
+ int free_json = 0;
877
1028
 
878
1029
  if (argc < 1) {
879
- rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
1030
+ rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
880
1031
  }
881
1032
  input = argv[0];
882
1033
  if (2 <= argc) {
883
- if (T_HASH == rb_type(argv[1])) {
884
- oj_parse_options(argv[1], &pi->options);
885
- } else if (3 <= argc && T_HASH == rb_type(argv[2])) {
886
- oj_parse_options(argv[2], &pi->options);
887
- }
1034
+ if (T_HASH == rb_type(argv[1])) {
1035
+ oj_parse_options(argv[1], &pi->options);
1036
+ } else if (3 <= argc && T_HASH == rb_type(argv[2])) {
1037
+ oj_parse_options(argv[2], &pi->options);
1038
+ }
888
1039
  }
889
1040
  if (yieldOk && rb_block_given_p()) {
890
- pi->proc = Qnil;
1041
+ pi->proc = Qnil;
891
1042
  } else {
892
- pi->proc = Qundef;
1043
+ pi->proc = Qundef;
893
1044
  }
894
1045
  if (0 != json) {
895
- pi->json = json;
896
- pi->end = json + len;
897
- free_json = 1;
1046
+ pi->json = json;
1047
+ pi->end = json + len;
1048
+ free_json = 1;
898
1049
  } else if (T_STRING == rb_type(input)) {
899
- if (CompatMode == pi->options.mode) {
900
- if (No == pi->options.nilnil && 0 == RSTRING_LEN(input)) {
901
- rb_raise(oj_json_parser_error_class, "An empty string is not a valid JSON string.");
902
- }
903
- }
904
- oj_pi_set_input_str(pi, &input);
1050
+ if (CompatMode == pi->options.mode) {
1051
+ if (No == pi->options.nilnil && 0 == RSTRING_LEN(input)) {
1052
+ rb_raise(oj_json_parser_error_class, "An empty string is not a valid JSON string.");
1053
+ }
1054
+ }
1055
+ oj_pi_set_input_str(pi, &input);
905
1056
  } else if (Qnil == input) {
906
- if (Yes == pi->options.nilnil) {
907
- return Qnil;
908
- } else {
909
- rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
910
- }
1057
+ if (Yes == pi->options.nilnil) {
1058
+ return Qnil;
1059
+ } else {
1060
+ rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
1061
+ }
911
1062
  } else {
912
- VALUE clas = rb_obj_class(input);
913
- volatile VALUE s;
1063
+ VALUE clas = rb_obj_class(input);
1064
+ VALUE s;
914
1065
 
915
- if (oj_stringio_class == clas) {
916
- s = rb_funcall2(input, oj_string_id, 0, 0);
917
- oj_pi_set_input_str(pi, &s);
1066
+ if (oj_stringio_class == clas) {
1067
+ s = rb_funcall2(input, oj_string_id, 0, 0);
1068
+ oj_pi_set_input_str(pi, &s);
918
1069
  #if !IS_WINDOWS
919
- } else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
920
- int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
921
- ssize_t cnt;
922
- size_t len = lseek(fd, 0, SEEK_END);
923
-
924
- lseek(fd, 0, SEEK_SET);
925
- buf = ALLOC_N(char, len + 1);
926
- pi->json = buf;
927
- pi->end = buf + len;
928
- if (0 >= (cnt = read(fd, (char*)pi->json, len)) || cnt != (ssize_t)len) {
929
- if (0 != buf) {
930
- xfree(buf);
931
- }
932
- rb_raise(rb_eIOError, "failed to read from IO Object.");
933
- }
934
- ((char*)pi->json)[len] = '\0';
935
- /* skip UTF-8 BOM if present */
936
- if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
937
- pi->cur += 3;
938
- }
1070
+ } else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
1071
+ int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
1072
+ ssize_t cnt;
1073
+ size_t len = lseek(fd, 0, SEEK_END);
1074
+
1075
+ lseek(fd, 0, SEEK_SET);
1076
+ buf = ALLOC_N(char, len + 1);
1077
+ pi->json = buf;
1078
+ pi->end = buf + len;
1079
+ if (0 >= (cnt = read(fd, (char *)pi->json, len)) || cnt != (ssize_t)len) {
1080
+ if (0 != buf) {
1081
+ xfree(buf);
1082
+ }
1083
+ rb_raise(rb_eIOError, "failed to read from IO Object.");
1084
+ }
1085
+ ((char *)pi->json)[len] = '\0';
1086
+ /* skip UTF-8 BOM if present */
1087
+ if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] &&
1088
+ 0xBF == (uint8_t)pi->json[2]) {
1089
+ pi->cur += 3;
1090
+ }
939
1091
  #endif
940
- } else if (rb_respond_to(input, oj_read_id)) {
941
- // use stream parser instead
942
- return oj_pi_sparse(argc, argv, pi, 0);
943
- } else {
944
- rb_raise(rb_eArgError, "parse() expected a String or IO Object.");
945
- }
1092
+ } else if (rb_respond_to(input, oj_read_id)) {
1093
+ // use stream parser instead
1094
+ return oj_pi_sparse(argc, argv, pi, 0);
1095
+ } else {
1096
+ rb_raise(rb_eArgError, "parse() expected a String or IO Object.");
1097
+ }
946
1098
  }
947
1099
  if (Yes == pi->options.circular) {
948
- pi->circ_array = oj_circ_array_new();
1100
+ pi->circ_array = oj_circ_array_new();
949
1101
  } else {
950
- pi->circ_array = 0;
1102
+ pi->circ_array = 0;
951
1103
  }
952
1104
  if (No == pi->options.allow_gc) {
953
- rb_gc_disable();
1105
+ rb_gc_disable();
954
1106
  }
955
1107
  // GC can run at any time. When it runs any Object created by C will be
956
1108
  // freed. We protect against this by wrapping the value stack in a ruby
@@ -959,112 +1111,116 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
959
1111
  wrapped_stack = oj_stack_init(&pi->stack);
960
1112
  rb_protect(protect_parse, (VALUE)pi, &line);
961
1113
  if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
962
- if (No == pi->options.nilnil || (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
963
- oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
964
- }
1114
+ if (No == pi->options.nilnil ||
1115
+ (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
1116
+ oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
1117
+ }
965
1118
  }
966
- result = stack_head_val(&pi->stack);
1119
+ result = stack_head_val(&pi->stack);
967
1120
  DATA_PTR(wrapped_stack) = 0;
968
1121
  if (No == pi->options.allow_gc) {
969
- rb_gc_enable();
1122
+ rb_gc_enable();
970
1123
  }
971
1124
  if (!err_has(&pi->err)) {
972
- // If the stack is not empty then the JSON terminated early.
973
- Val v;
974
- VALUE err_class = oj_parse_error_class;
975
-
976
- if (0 != line) {
977
- VALUE ec = rb_obj_class(rb_errinfo());
978
-
979
- if (rb_eIOError != ec) {
980
- goto CLEANUP;
981
- }
982
- if (rb_eArgError != ec && 0 != ec) {
983
- err_class = ec;
984
- }
985
- }
986
- if (NULL != (v = stack_peek(&pi->stack))) {
987
- switch (v->next) {
988
- case NEXT_ARRAY_NEW:
989
- case NEXT_ARRAY_ELEMENT:
990
- case NEXT_ARRAY_COMMA:
991
- oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated");
992
- break;
993
- case NEXT_HASH_NEW:
994
- case NEXT_HASH_KEY:
995
- case NEXT_HASH_COLON:
996
- case NEXT_HASH_VALUE:
997
- case NEXT_HASH_COMMA:
998
- oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Hash/Object not terminated");
999
- break;
1000
- default:
1001
- oj_set_error_at(pi, err_class, __FILE__, __LINE__, "not terminated");
1002
- }
1003
- }
1125
+ // If the stack is not empty then the JSON terminated early.
1126
+ Val v;
1127
+ VALUE err_class = oj_parse_error_class;
1128
+
1129
+ if (0 != line) {
1130
+ VALUE ec = rb_obj_class(rb_errinfo());
1131
+
1132
+ if (rb_eArgError != ec && 0 != ec) {
1133
+ err_class = ec;
1134
+ }
1135
+ if (rb_eIOError != ec) {
1136
+ goto CLEANUP;
1137
+ }
1138
+ }
1139
+ if (NULL != (v = stack_peek(&pi->stack))) {
1140
+ switch (v->next) {
1141
+ case NEXT_ARRAY_NEW:
1142
+ case NEXT_ARRAY_ELEMENT:
1143
+ case NEXT_ARRAY_COMMA:
1144
+ oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated");
1145
+ break;
1146
+ case NEXT_HASH_NEW:
1147
+ case NEXT_HASH_KEY:
1148
+ case NEXT_HASH_COLON:
1149
+ case NEXT_HASH_VALUE:
1150
+ case NEXT_HASH_COMMA:
1151
+ oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Hash/Object not terminated");
1152
+ break;
1153
+ default: oj_set_error_at(pi, err_class, __FILE__, __LINE__, "not terminated");
1154
+ }
1155
+ }
1004
1156
  }
1005
1157
  CLEANUP:
1006
1158
  // proceed with cleanup
1007
1159
  if (0 != pi->circ_array) {
1008
- oj_circ_array_free(pi->circ_array);
1160
+ oj_circ_array_free(pi->circ_array);
1009
1161
  }
1010
1162
  if (0 != buf) {
1011
- xfree(buf);
1163
+ xfree(buf);
1012
1164
  } else if (free_json) {
1013
- xfree(json);
1165
+ xfree(json);
1014
1166
  }
1015
1167
  stack_cleanup(&pi->stack);
1016
1168
  if (pi->str_rx.head != oj_default_options.str_rx.head) {
1017
- oj_rxclass_cleanup(&pi->str_rx);
1169
+ oj_rxclass_cleanup(&pi->str_rx);
1018
1170
  }
1019
1171
  if (err_has(&pi->err)) {
1020
- rb_set_errinfo(Qnil);
1021
- if (Qnil != pi->err_class) {
1022
- pi->err.clas = pi->err_class;
1023
- }
1024
- if (CompatMode == pi->options.mode) {
1025
- // The json gem requires the error message be UTF-8 encoded. In
1026
- // additional the complete JSON source must be returned. There
1027
- // does not seem to be a size limit.
1028
- VALUE msg = oj_encode(rb_str_new2(pi->err.msg));
1029
- VALUE args[1];
1030
-
1031
- if (NULL != pi->json) {
1032
- msg = rb_str_append(msg, oj_encode(rb_str_new2(" in '")));
1033
- msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
1034
- }
1035
- args[0] = msg;
1036
- rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
1037
- } else {
1038
- oj_err_raise(&pi->err);
1039
- }
1172
+ rb_set_errinfo(Qnil);
1173
+ if (Qnil != pi->err_class) {
1174
+ pi->err.clas = pi->err_class;
1175
+ }
1176
+ if ((CompatMode == pi->options.mode || RailsMode == pi->options.mode) && Yes != pi->options.safe) {
1177
+ // The json gem requires the error message be UTF-8 encoded. In
1178
+ // additional the complete JSON source must be returned. There
1179
+ // does not seem to be a size limit.
1180
+ VALUE msg = oj_encode(rb_str_new2(pi->err.msg));
1181
+ VALUE args[1];
1182
+
1183
+ if (NULL != pi->json) {
1184
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(" in '")));
1185
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
1186
+ }
1187
+ args[0] = msg;
1188
+ if (pi->err.clas == oj_parse_error_class) {
1189
+ // The error was an Oj::ParseError so change to a JSON::ParserError.
1190
+ pi->err.clas = oj_json_parser_error_class;
1191
+ }
1192
+ rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
1193
+ } else {
1194
+ oj_err_raise(&pi->err);
1195
+ }
1040
1196
  } else if (0 != line) {
1041
- rb_jump_tag(line);
1197
+ rb_jump_tag(line);
1042
1198
  }
1043
1199
  if (pi->options.quirks_mode == No) {
1044
- switch (rb_type(result)) {
1045
- case T_NIL:
1046
- case T_TRUE:
1047
- case T_FALSE:
1048
- case T_FIXNUM:
1049
- case T_FLOAT:
1050
- case T_CLASS:
1051
- case T_STRING:
1052
- case T_SYMBOL: {
1053
- struct _Err err;
1054
-
1055
- if (Qnil == pi->err_class) {
1056
- err.clas = oj_parse_error_class;
1057
- } else {
1058
- err.clas = pi->err_class;
1059
- }
1060
- snprintf(err.msg, sizeof(err.msg), "unexpected non-document value");
1061
- oj_err_raise(&err);
1062
- break;
1063
- }
1064
- default:
1065
- // okay
1066
- break;
1067
- }
1200
+ switch (rb_type(result)) {
1201
+ case T_NIL:
1202
+ case T_TRUE:
1203
+ case T_FALSE:
1204
+ case T_FIXNUM:
1205
+ case T_FLOAT:
1206
+ case T_CLASS:
1207
+ case T_STRING:
1208
+ case T_SYMBOL: {
1209
+ struct _err err;
1210
+
1211
+ if (Qnil == pi->err_class) {
1212
+ err.clas = oj_parse_error_class;
1213
+ } else {
1214
+ err.clas = pi->err_class;
1215
+ }
1216
+ snprintf(err.msg, sizeof(err.msg), "unexpected non-document value");
1217
+ oj_err_raise(&err);
1218
+ break;
1219
+ }
1220
+ default:
1221
+ // okay
1222
+ break;
1223
+ }
1068
1224
  }
1069
1225
  return result;
1070
1226
  }