oj 3.7.4 → 3.13.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1360 -0
  3. data/README.md +31 -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 +790 -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 +1073 -1088
  29. data/ext/oj/intern.c +298 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +469 -436
  32. data/ext/oj/object.c +532 -599
  33. data/ext/oj/odd.c +154 -138
  34. data/ext/oj/odd.h +37 -38
  35. data/ext/oj/oj.c +1333 -986
  36. data/ext/oj/oj.h +336 -316
  37. data/ext/oj/parse.c +1002 -846
  38. data/ext/oj/parse.h +92 -87
  39. data/ext/oj/parser.c +1587 -0
  40. data/ext/oj/parser.h +102 -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 +596 -0
  51. data/ext/oj/saj2.h +23 -0
  52. data/ext/oj/scp.c +88 -113
  53. data/ext/oj/sparse.c +787 -709
  54. data/ext/oj/stream_writer.c +133 -159
  55. data/ext/oj/strict.c +127 -118
  56. data/ext/oj/string_writer.c +230 -249
  57. data/ext/oj/trace.c +34 -41
  58. data/ext/oj/trace.h +19 -19
  59. data/ext/oj/usual.c +1207 -0
  60. data/ext/oj/usual.h +68 -0
  61. data/ext/oj/util.c +136 -0
  62. data/ext/oj/util.h +20 -0
  63. data/ext/oj/val_stack.c +60 -68
  64. data/ext/oj/val_stack.h +91 -129
  65. data/ext/oj/validate.c +46 -0
  66. data/ext/oj/wab.c +342 -353
  67. data/lib/oj/bag.rb +1 -0
  68. data/lib/oj/easy_hash.rb +5 -4
  69. data/lib/oj/error.rb +1 -1
  70. data/lib/oj/json.rb +1 -1
  71. data/lib/oj/mimic.rb +48 -14
  72. data/lib/oj/saj.rb +20 -6
  73. data/lib/oj/state.rb +9 -8
  74. data/lib/oj/version.rb +2 -2
  75. data/lib/oj.rb +0 -8
  76. data/pages/Compatibility.md +1 -1
  77. data/pages/JsonGem.md +15 -0
  78. data/pages/Modes.md +53 -46
  79. data/pages/Options.md +78 -11
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +73 -22
  82. data/pages/Security.md +1 -1
  83. data/test/activerecord/result_test.rb +7 -2
  84. data/test/activesupport5/abstract_unit.rb +45 -0
  85. data/test/activesupport5/decoding_test.rb +68 -60
  86. data/test/activesupport5/encoding_test.rb +111 -96
  87. data/test/activesupport5/encoding_test_cases.rb +33 -25
  88. data/test/activesupport5/test_helper.rb +43 -21
  89. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  90. data/test/activesupport6/abstract_unit.rb +44 -0
  91. data/test/activesupport6/decoding_test.rb +133 -0
  92. data/test/activesupport6/encoding_test.rb +507 -0
  93. data/test/activesupport6/encoding_test_cases.rb +98 -0
  94. data/test/activesupport6/test_common.rb +17 -0
  95. data/test/activesupport6/test_helper.rb +163 -0
  96. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  97. data/test/activesupport7/abstract_unit.rb +49 -0
  98. data/test/activesupport7/decoding_test.rb +125 -0
  99. data/test/activesupport7/encoding_test.rb +486 -0
  100. data/test/activesupport7/encoding_test_cases.rb +104 -0
  101. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  102. data/test/bar.rb +6 -12
  103. data/test/baz.rb +16 -0
  104. data/test/bug.rb +16 -0
  105. data/test/foo.rb +69 -75
  106. data/test/helper.rb +16 -0
  107. data/test/json_gem/json_common_interface_test.rb +8 -3
  108. data/test/json_gem/json_generator_test.rb +21 -8
  109. data/test/json_gem/json_parser_test.rb +8 -1
  110. data/test/json_gem/test_helper.rb +12 -0
  111. data/test/mem.rb +33 -0
  112. data/test/perf.rb +1 -1
  113. data/test/perf_dump.rb +50 -0
  114. data/test/perf_once.rb +58 -0
  115. data/test/perf_parser.rb +189 -0
  116. data/test/perf_scp.rb +11 -10
  117. data/test/perf_strict.rb +17 -23
  118. data/test/prec.rb +23 -0
  119. data/test/sample_json.rb +1 -1
  120. data/test/test_compat.rb +46 -10
  121. data/test/test_custom.rb +145 -7
  122. data/test/test_fast.rb +62 -2
  123. data/test/test_file.rb +23 -7
  124. data/test/test_gc.rb +11 -0
  125. data/test/test_generate.rb +21 -0
  126. data/test/test_hash.rb +11 -1
  127. data/test/test_integer_range.rb +1 -2
  128. data/test/test_object.rb +43 -12
  129. data/test/test_parser.rb +11 -0
  130. data/test/test_parser_debug.rb +27 -0
  131. data/test/test_parser_saj.rb +335 -0
  132. data/test/test_parser_usual.rb +217 -0
  133. data/test/test_rails.rb +35 -0
  134. data/test/test_saj.rb +1 -1
  135. data/test/test_scp.rb +3 -5
  136. data/test/test_strict.rb +26 -1
  137. data/test/test_various.rb +86 -65
  138. data/test/test_wab.rb +2 -0
  139. data/test/test_writer.rb +19 -2
  140. data/test/tests.rb +10 -1
  141. data/test/tests_mimic.rb +9 -0
  142. data/test/tests_mimic_addition.rb +9 -0
  143. data/test/zoo.rb +13 -0
  144. metadata +63 -110
  145. data/ext/oj/hash.c +0 -163
  146. data/ext/oj/hash.h +0 -46
  147. 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
  }