oj 2.0.0 → 3.0.0

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 (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
data/ext/oj/sparse.c ADDED
@@ -0,0 +1,911 @@
1
+ /* parse.c
2
+ * Copyright (c) 2013, Peter Ohler
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * - Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * - Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * - Neither the name of Peter Ohler nor the names of its contributors may be
16
+ * used to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #include <stdlib.h>
32
+ #include <stdio.h>
33
+ #include <string.h>
34
+ #include <unistd.h>
35
+ #include <math.h>
36
+
37
+ #include "oj.h"
38
+ #include "encode.h"
39
+ #include "parse.h"
40
+ #include "buf.h"
41
+ #include "hash.h" // for oj_strndup()
42
+ #include "val_stack.h"
43
+
44
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
45
+ #define OJ_INFINITY (1.0/0.0)
46
+
47
+ #ifdef RUBINIUS_RUBY
48
+ #define NUM_MAX 0x07FFFFFF
49
+ #else
50
+ #define NUM_MAX (FIXNUM_MAX >> 8)
51
+ #endif
52
+ #define EXP_MAX 100000
53
+ #define DEC_MAX 15
54
+
55
+ static void
56
+ skip_comment(ParseInfo pi) {
57
+ char c = reader_get(&pi->rd);
58
+
59
+ if ('*' == c) {
60
+ while ('\0' != (c = reader_get(&pi->rd))) {
61
+ if ('*' == c) {
62
+ c = reader_get(&pi->rd);
63
+ if ('/' == c) {
64
+ return;
65
+ }
66
+ }
67
+ }
68
+ } else if ('/' == c) {
69
+ while ('\0' != (c = reader_get(&pi->rd))) {
70
+ switch (c) {
71
+ case '\n':
72
+ case '\r':
73
+ case '\f':
74
+ case '\0':
75
+ return;
76
+ default:
77
+ break;
78
+ }
79
+ }
80
+ } else {
81
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
82
+ }
83
+ if ('\0' == c) {
84
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
85
+ return;
86
+ }
87
+ }
88
+
89
+ static void
90
+ add_value(ParseInfo pi, VALUE rval) {
91
+ Val parent = stack_peek(&pi->stack);
92
+
93
+ if (0 == parent) { // simple add
94
+ pi->add_value(pi, rval);
95
+ } else {
96
+ switch (parent->next) {
97
+ case NEXT_ARRAY_NEW:
98
+ case NEXT_ARRAY_ELEMENT:
99
+ pi->array_append_value(pi, rval);
100
+ parent->next = NEXT_ARRAY_COMMA;
101
+ break;
102
+ case NEXT_HASH_VALUE:
103
+ pi->hash_set_value(pi, parent, rval);
104
+ if (parent->kalloc) {
105
+ xfree((char*)parent->key);
106
+ }
107
+ parent->key = 0;
108
+ parent->kalloc = 0;
109
+ parent->next = NEXT_HASH_COMMA;
110
+ break;
111
+ case NEXT_HASH_NEW:
112
+ case NEXT_HASH_KEY:
113
+ case NEXT_HASH_COMMA:
114
+ case NEXT_NONE:
115
+ case NEXT_ARRAY_COMMA:
116
+ case NEXT_HASH_COLON:
117
+ default:
118
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
119
+ break;
120
+ }
121
+ }
122
+ }
123
+
124
+ static void
125
+ add_num_value(ParseInfo pi, NumInfo ni) {
126
+ Val parent = stack_peek(&pi->stack);
127
+
128
+ if (0 == parent) {
129
+ pi->add_num(pi, ni);
130
+ } else {
131
+ switch (parent->next) {
132
+ case NEXT_ARRAY_NEW:
133
+ case NEXT_ARRAY_ELEMENT:
134
+ pi->array_append_num(pi, ni);
135
+ parent->next = NEXT_ARRAY_COMMA;
136
+ break;
137
+ case NEXT_HASH_VALUE:
138
+ pi->hash_set_num(pi, parent, ni);
139
+ if (parent->kalloc) {
140
+ xfree((char*)parent->key);
141
+ }
142
+ parent->key = 0;
143
+ parent->kalloc = 0;
144
+ parent->next = NEXT_HASH_COMMA;
145
+ break;
146
+ default:
147
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
148
+ break;
149
+ }
150
+ }
151
+ }
152
+
153
+ static void
154
+ read_true(ParseInfo pi) {
155
+ if (0 == reader_expect(&pi->rd, "rue")) {
156
+ add_value(pi, Qtrue);
157
+ } else {
158
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
159
+ }
160
+ }
161
+
162
+ static void
163
+ read_false(ParseInfo pi) {
164
+ if (0 == reader_expect(&pi->rd, "alse")) {
165
+ add_value(pi, Qfalse);
166
+ } else {
167
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
168
+ }
169
+ }
170
+
171
+ static uint32_t
172
+ read_hex(ParseInfo pi) {
173
+ uint32_t b = 0;
174
+ int i;
175
+ char c;
176
+
177
+ for (i = 0; i < 4; i++) {
178
+ c = reader_get(&pi->rd);
179
+ b = b << 4;
180
+ if ('0' <= c && c <= '9') {
181
+ b += c - '0';
182
+ } else if ('A' <= c && c <= 'F') {
183
+ b += c - 'A' + 10;
184
+ } else if ('a' <= c && c <= 'f') {
185
+ b += c - 'a' + 10;
186
+ } else {
187
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
188
+ return 0;
189
+ }
190
+ }
191
+ return b;
192
+ }
193
+
194
+ static void
195
+ unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
196
+ if (0x0000007F >= code) {
197
+ buf_append(buf, (char)code);
198
+ } else if (0x000007FF >= code) {
199
+ buf_append(buf, 0xC0 | (code >> 6));
200
+ buf_append(buf, 0x80 | (0x3F & code));
201
+ } else if (0x0000FFFF >= code) {
202
+ buf_append(buf, 0xE0 | (code >> 12));
203
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
204
+ buf_append(buf, 0x80 | (0x3F & code));
205
+ } else if (0x001FFFFF >= code) {
206
+ buf_append(buf, 0xF0 | (code >> 18));
207
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
208
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
209
+ buf_append(buf, 0x80 | (0x3F & code));
210
+ } else if (0x03FFFFFF >= code) {
211
+ buf_append(buf, 0xF8 | (code >> 24));
212
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
213
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
214
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
215
+ buf_append(buf, 0x80 | (0x3F & code));
216
+ } else if (0x7FFFFFFF >= code) {
217
+ buf_append(buf, 0xFC | (code >> 30));
218
+ buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
219
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
220
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
221
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
222
+ buf_append(buf, 0x80 | (0x3F & code));
223
+ } else {
224
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
225
+ }
226
+ }
227
+
228
+ // entered at backslash
229
+ static void
230
+ read_escaped_str(ParseInfo pi) {
231
+ struct _Buf buf;
232
+ char c;
233
+ uint32_t code;
234
+ Val parent = stack_peek(&pi->stack);
235
+
236
+ buf_init(&buf);
237
+ if (pi->rd.str < pi->rd.tail) {
238
+ buf_append_string(&buf, pi->rd.str, pi->rd.tail - pi->rd.str);
239
+ }
240
+ while ('\"' != (c = reader_get(&pi->rd))) {
241
+ if ('\0' == c) {
242
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
243
+ buf_cleanup(&buf);
244
+ return;
245
+ } else if ('\\' == c) {
246
+ c = reader_get(&pi->rd);
247
+ switch (c) {
248
+ case 'n': buf_append(&buf, '\n'); break;
249
+ case 'r': buf_append(&buf, '\r'); break;
250
+ case 't': buf_append(&buf, '\t'); break;
251
+ case 'f': buf_append(&buf, '\f'); break;
252
+ case 'b': buf_append(&buf, '\b'); break;
253
+ case '"': buf_append(&buf, '"'); break;
254
+ case '/': buf_append(&buf, '/'); break;
255
+ case '\\': buf_append(&buf, '\\'); break;
256
+ case '\'':
257
+ // The json gem claims this is not an error despite the
258
+ // ECMA-404 indicating it is not valid.
259
+ if (CompatMode == pi->options.mode) {
260
+ buf_append(&buf, '\'');
261
+ } else {
262
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
263
+ buf_cleanup(&buf);
264
+ return;
265
+ }
266
+ break;
267
+ case 'u':
268
+ if (0 == (code = read_hex(pi)) && err_has(&pi->err)) {
269
+ buf_cleanup(&buf);
270
+ return;
271
+ }
272
+ if (0x0000D800 <= code && code <= 0x0000DFFF) {
273
+ uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
274
+ uint32_t c2;
275
+ char ch2;
276
+
277
+ c = reader_get(&pi->rd);
278
+ ch2 = reader_get(&pi->rd);
279
+ if ('\\' != c || 'u' != ch2) {
280
+ if (Yes == pi->options.allow_invalid) {
281
+ unicode_to_chars(pi, &buf, code);
282
+ reader_backup(&pi->rd);
283
+ reader_backup(&pi->rd);
284
+ break;
285
+ }
286
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
287
+ buf_cleanup(&buf);
288
+ return;
289
+ }
290
+ if (0 == (c2 = read_hex(pi)) && err_has(&pi->err)) {
291
+ buf_cleanup(&buf);
292
+ return;
293
+ }
294
+ c2 = (c2 - 0x0000DC00) & 0x000003FF;
295
+ code = ((c1 << 10) | c2) + 0x00010000;
296
+ }
297
+ unicode_to_chars(pi, &buf, code);
298
+ if (err_has(&pi->err)) {
299
+ buf_cleanup(&buf);
300
+ return;
301
+ }
302
+ break;
303
+ default:
304
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
305
+ buf_cleanup(&buf);
306
+ return;
307
+ }
308
+ } else {
309
+ buf_append(&buf, c);
310
+ }
311
+ }
312
+ if (0 == parent) {
313
+ pi->add_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
314
+ } else {
315
+ switch (parent->next) {
316
+ case NEXT_ARRAY_NEW:
317
+ case NEXT_ARRAY_ELEMENT:
318
+ pi->array_append_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
319
+ parent->next = NEXT_ARRAY_COMMA;
320
+ break;
321
+ case NEXT_HASH_NEW:
322
+ case NEXT_HASH_KEY:
323
+ if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
324
+ parent->key = strdup(buf.head);
325
+ parent->klen = buf_len(&buf);
326
+ } else {
327
+ parent->key = "";
328
+ parent->klen = 0;
329
+ }
330
+ parent->k1 = *pi->rd.str;
331
+ parent->next = NEXT_HASH_COLON;
332
+ break;
333
+ case NEXT_HASH_VALUE:
334
+ pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), pi->rd.str);
335
+ if (parent->kalloc) {
336
+ xfree((char*)parent->key);
337
+ }
338
+ parent->key = 0;
339
+ parent->kalloc = 0;
340
+ parent->next = NEXT_HASH_COMMA;
341
+ break;
342
+ case NEXT_HASH_COMMA:
343
+ case NEXT_NONE:
344
+ case NEXT_ARRAY_COMMA:
345
+ case NEXT_HASH_COLON:
346
+ default:
347
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
348
+ break;
349
+ }
350
+ }
351
+ buf_cleanup(&buf);
352
+ }
353
+
354
+ static void
355
+ read_str(ParseInfo pi) {
356
+ Val parent = stack_peek(&pi->stack);
357
+ char c;
358
+
359
+ reader_protect(&pi->rd);
360
+ while ('\"' != (c = reader_get(&pi->rd))) {
361
+ if ('\0' == c) {
362
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
363
+ return;
364
+ } else if ('\\' == c) {
365
+ reader_backup(&pi->rd);
366
+ read_escaped_str(pi);
367
+ reader_release(&pi->rd);
368
+ return;
369
+ }
370
+ }
371
+ if (0 == parent) { // simple add
372
+ pi->add_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
373
+ } else {
374
+ switch (parent->next) {
375
+ case NEXT_ARRAY_NEW:
376
+ case NEXT_ARRAY_ELEMENT:
377
+ pi->array_append_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
378
+ parent->next = NEXT_ARRAY_COMMA;
379
+ break;
380
+ case NEXT_HASH_NEW:
381
+ case NEXT_HASH_KEY:
382
+ parent->klen = pi->rd.tail - pi->rd.str - 1;
383
+ if (sizeof(parent->karray) <= parent->klen) {
384
+ parent->key = oj_strndup(pi->rd.str, parent->klen);
385
+ parent->kalloc = 1;
386
+ } else {
387
+ memcpy(parent->karray, pi->rd.str, parent->klen);
388
+ parent->karray[parent->klen] = '\0';
389
+ parent->key = parent->karray;
390
+ parent->kalloc = 0;
391
+ }
392
+ parent->key_val = pi->hash_key(pi, parent->key, parent->klen);
393
+ parent->k1 = *pi->rd.str;
394
+ parent->next = NEXT_HASH_COLON;
395
+ break;
396
+ case NEXT_HASH_VALUE:
397
+ pi->hash_set_cstr(pi, parent, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
398
+ if (parent->kalloc) {
399
+ xfree((char*)parent->key);
400
+ }
401
+ parent->key = 0;
402
+ parent->kalloc = 0;
403
+ parent->next = NEXT_HASH_COMMA;
404
+ break;
405
+ case NEXT_HASH_COMMA:
406
+ case NEXT_NONE:
407
+ case NEXT_ARRAY_COMMA:
408
+ case NEXT_HASH_COLON:
409
+ default:
410
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
411
+ break;
412
+ }
413
+ }
414
+ reader_release(&pi->rd);
415
+ }
416
+
417
+ static void
418
+ read_num(ParseInfo pi) {
419
+ struct _NumInfo ni;
420
+ char c;
421
+
422
+ reader_protect(&pi->rd);
423
+ ni.i = 0;
424
+ ni.num = 0;
425
+ ni.div = 1;
426
+ ni.di = 0;
427
+ ni.len = 0;
428
+ ni.exp = 0;
429
+ ni.big = 0;
430
+ ni.infinity = 0;
431
+ ni.nan = 0;
432
+ ni.neg = 0;
433
+ ni.hasExp = 0;
434
+ ni.no_big = (FloatDec == pi->options.bigdec_load);
435
+ c = reader_get(&pi->rd);
436
+ if ('-' == c) {
437
+ c = reader_get(&pi->rd);
438
+ ni.neg = 1;
439
+ } else if ('+' == c) {
440
+ c = reader_get(&pi->rd);
441
+ }
442
+ if ('I' == c) {
443
+ if (No == pi->options.allow_nan) {
444
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
445
+ return;
446
+ } else if (0 != reader_expect(&pi->rd, "nfinity")) {
447
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
448
+ return;
449
+ }
450
+ ni.infinity = 1;
451
+ } else {
452
+ int dec_cnt = 0;
453
+ bool zero1 = false;
454
+
455
+ for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
456
+ if (0 == ni.i && '0' == c) {
457
+ zero1 = true;
458
+ }
459
+ if (0 < ni.i) {
460
+ dec_cnt++;
461
+ }
462
+ if (ni.big) {
463
+ ni.big++;
464
+ } else {
465
+ int d = (c - '0');
466
+
467
+ if (0 < d) {
468
+ if (zero1 && CompatMode == pi->options.mode) {
469
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
470
+ return;
471
+ }
472
+ zero1 = false;
473
+ }
474
+ ni.i = ni.i * 10 + d;
475
+ if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
476
+ ni.big = 1;
477
+ }
478
+ }
479
+ }
480
+ if ('.' == c) {
481
+ c = reader_get(&pi->rd);
482
+ if (c < '0' || '9' < c) {
483
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
484
+ }
485
+ for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
486
+ int d = (c - '0');
487
+
488
+ if (0 < ni.num || 0 < ni.i) {
489
+ dec_cnt++;
490
+ }
491
+ ni.num = ni.num * 10 + d;
492
+ ni.div *= 10;
493
+ ni.di++;
494
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
495
+ ni.big = 1;
496
+ }
497
+ }
498
+ }
499
+ if ('e' == c || 'E' == c) {
500
+ int eneg = 0;
501
+
502
+ ni.hasExp = 1;
503
+ c = reader_get(&pi->rd);
504
+ if ('-' == c) {
505
+ c = reader_get(&pi->rd);
506
+ eneg = 1;
507
+ } else if ('+' == c) {
508
+ c = reader_get(&pi->rd);
509
+ }
510
+ for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
511
+ ni.exp = ni.exp * 10 + (c - '0');
512
+ if (EXP_MAX <= ni.exp) {
513
+ ni.big = 1;
514
+ }
515
+ }
516
+ if (eneg) {
517
+ ni.exp = -ni.exp;
518
+ }
519
+ }
520
+ ni.len = pi->rd.tail - pi->rd.str;
521
+ if (0 != c) {
522
+ reader_backup(&pi->rd);
523
+ }
524
+ }
525
+ ni.str = pi->rd.str;
526
+ ni.len = pi->rd.tail - pi->rd.str;
527
+ // Check for special reserved values for Infinity and NaN.
528
+ if (ni.big) {
529
+ if (0 == strcasecmp(INF_VAL, ni.str)) {
530
+ ni.infinity = 1;
531
+ } else if (0 == strcasecmp(NINF_VAL, ni.str)) {
532
+ ni.infinity = 1;
533
+ ni.neg = 1;
534
+ } else if (0 == strcasecmp(NAN_VAL, ni.str)) {
535
+ ni.nan = 1;
536
+ }
537
+ }
538
+ if (BigDec == pi->options.bigdec_load) {
539
+ ni.big = 1;
540
+ }
541
+ add_num_value(pi, &ni);
542
+ reader_release(&pi->rd);
543
+ }
544
+
545
+ static void
546
+ read_nan(ParseInfo pi) {
547
+ struct _NumInfo ni;
548
+ char c;
549
+
550
+ ni.str = pi->rd.str;
551
+ ni.i = 0;
552
+ ni.num = 0;
553
+ ni.div = 1;
554
+ ni.di = 0;
555
+ ni.len = 0;
556
+ ni.exp = 0;
557
+ ni.big = 0;
558
+ ni.infinity = 0;
559
+ ni.nan = 1;
560
+ ni.neg = 0;
561
+ ni.no_big = (FloatDec == pi->options.bigdec_load);
562
+
563
+ if ('a' != reader_get(&pi->rd) ||
564
+ ('N' != (c = reader_get(&pi->rd)) && 'n' != c)) {
565
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
566
+ return;
567
+ }
568
+ if (BigDec == pi->options.bigdec_load) {
569
+ ni.big = 1;
570
+ }
571
+ add_num_value(pi, &ni);
572
+ }
573
+
574
+ static void
575
+ array_start(ParseInfo pi) {
576
+ VALUE v = pi->start_array(pi);
577
+
578
+ stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
579
+ }
580
+
581
+ static void
582
+ array_end(ParseInfo pi) {
583
+ Val array = stack_pop(&pi->stack);
584
+
585
+ if (0 == array) {
586
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
587
+ } else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
588
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not an array close", oj_stack_next_string(array->next));
589
+ } else {
590
+ pi->end_array(pi);
591
+ add_value(pi, array->val);
592
+ }
593
+ }
594
+
595
+ static void
596
+ hash_start(ParseInfo pi) {
597
+ volatile VALUE v = pi->start_hash(pi);
598
+
599
+ stack_push(&pi->stack, v, NEXT_HASH_NEW);
600
+ }
601
+
602
+ static void
603
+ hash_end(ParseInfo pi) {
604
+ volatile Val hash = stack_peek(&pi->stack);
605
+
606
+ // leave hash on stack until just before
607
+ if (0 == hash) {
608
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
609
+ } else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
610
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a hash close", oj_stack_next_string(hash->next));
611
+ } else {
612
+ pi->end_hash(pi);
613
+ stack_pop(&pi->stack);
614
+ add_value(pi, hash->val);
615
+ }
616
+ }
617
+
618
+ static void
619
+ comma(ParseInfo pi) {
620
+ Val parent = stack_peek(&pi->stack);
621
+
622
+ if (0 == parent) {
623
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
624
+ } else if (NEXT_ARRAY_COMMA == parent->next) {
625
+ parent->next = NEXT_ARRAY_ELEMENT;
626
+ } else if (NEXT_HASH_COMMA == parent->next) {
627
+ parent->next = NEXT_HASH_KEY;
628
+ } else {
629
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
630
+ }
631
+ }
632
+
633
+ static void
634
+ colon(ParseInfo pi) {
635
+ Val parent = stack_peek(&pi->stack);
636
+
637
+ if (0 != parent && NEXT_HASH_COLON == parent->next) {
638
+ parent->next = NEXT_HASH_VALUE;
639
+ } else {
640
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
641
+ }
642
+ }
643
+
644
+ void
645
+ oj_sparse2(ParseInfo pi) {
646
+ int first = 1;
647
+ char c;
648
+ long start = 0;
649
+
650
+ err_init(&pi->err);
651
+ while (1) {
652
+ if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
653
+ VALUE err_clas = oj_get_json_err_class("NestingError");
654
+
655
+ oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
656
+ pi->err_class = err_clas;
657
+ return;
658
+ }
659
+ c = reader_next_non_white(&pi->rd);
660
+ if (!first && '\0' != c) {
661
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
662
+ }
663
+ switch (c) {
664
+ case '{':
665
+ hash_start(pi);
666
+ break;
667
+ case '}':
668
+ hash_end(pi);
669
+ break;
670
+ case ':':
671
+ colon(pi);
672
+ break;
673
+ case '[':
674
+ array_start(pi);
675
+ break;
676
+ case ']':
677
+ array_end(pi);
678
+ break;
679
+ case ',':
680
+ comma(pi);
681
+ break;
682
+ case '"':
683
+ read_str(pi);
684
+ break;
685
+ case '+':
686
+ case '-':
687
+ case '0':
688
+ case '1':
689
+ case '2':
690
+ case '3':
691
+ case '4':
692
+ case '5':
693
+ case '6':
694
+ case '7':
695
+ case '8':
696
+ case '9':
697
+ reader_backup(&pi->rd);
698
+ read_num(pi);
699
+ break;
700
+ case 'I':
701
+ if (Yes == pi->options.allow_nan) {
702
+ reader_backup(&pi->rd);
703
+ read_num(pi);
704
+ } else {
705
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
706
+ return;
707
+ }
708
+ break;
709
+ case 'N':
710
+ if (Yes == pi->options.allow_nan) {
711
+ read_nan(pi);
712
+ } else {
713
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
714
+ return;
715
+ }
716
+ break;
717
+ case 't':
718
+ read_true(pi);
719
+ break;
720
+ case 'f':
721
+ read_false(pi);
722
+ break;
723
+ case 'n':
724
+ c = reader_get(&pi->rd);
725
+ if ('u' == c) {
726
+ if (0 == reader_expect(&pi->rd, "ll")) {
727
+ add_value(pi, Qnil);
728
+ } else {
729
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
730
+ return;
731
+ }
732
+ } else if ('a' == c) {
733
+ struct _NumInfo ni;
734
+
735
+ c = reader_get(&pi->rd);
736
+ if ('N' != c && 'n' != c) {
737
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected NaN");
738
+ return;
739
+ }
740
+ ni.str = pi->rd.str;
741
+ ni.i = 0;
742
+ ni.num = 0;
743
+ ni.div = 1;
744
+ ni.di = 0;
745
+ ni.len = 0;
746
+ ni.exp = 0;
747
+ ni.big = 0;
748
+ ni.infinity = 0;
749
+ ni.nan = 1;
750
+ ni.neg = 0;
751
+ ni.no_big = (FloatDec == pi->options.bigdec_load);
752
+ add_num_value(pi, &ni);
753
+ } else {
754
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid token");
755
+ return;
756
+ }
757
+ break;
758
+ case '/':
759
+ skip_comment(pi);
760
+ break;
761
+ case '\0':
762
+ return;
763
+ default:
764
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
765
+ return;
766
+ }
767
+ if (err_has(&pi->err)) {
768
+ return;
769
+ }
770
+ if (stack_empty(&pi->stack)) {
771
+ if (Qundef != pi->proc) {
772
+ VALUE args[3];
773
+ long len = pi->rd.pos - start;
774
+
775
+ *args = stack_head_val(&pi->stack);
776
+ args[1] = LONG2NUM(start);
777
+ args[2] = LONG2NUM(len);
778
+
779
+ if (Qnil == pi->proc) {
780
+ rb_yield_values2(3, args);
781
+ } else {
782
+ #if HAS_PROC_WITH_BLOCK
783
+ rb_proc_call_with_block(pi->proc, 3, args, Qnil);
784
+ #else
785
+ oj_set_error_at(pi, rb_eNotImpError, __FILE__, __LINE__,
786
+ "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
787
+ return;
788
+ #endif
789
+ }
790
+ } else if (!pi->has_callbacks) {
791
+ first = 0;
792
+ }
793
+ start = pi->rd.pos;
794
+ }
795
+ }
796
+ }
797
+
798
+ static VALUE
799
+ protect_parse(VALUE pip) {
800
+ oj_sparse2((ParseInfo)pip);
801
+
802
+ return Qnil;
803
+ }
804
+
805
+ VALUE
806
+ oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
807
+ volatile VALUE input;
808
+ volatile VALUE wrapped_stack;
809
+ VALUE result = Qnil;
810
+ int line = 0;
811
+
812
+ if (argc < 1) {
813
+ rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
814
+ }
815
+ input = argv[0];
816
+ if (2 <= argc) {
817
+ if (T_HASH == rb_type(argv[1])) {
818
+ oj_parse_options(argv[1], &pi->options);
819
+ } else if (3 <= argc && T_HASH == rb_type(argv[2])) {
820
+ oj_parse_options(argv[2], &pi->options);
821
+ }
822
+ }
823
+ if (Qnil == input) {
824
+ if (Yes == pi->options.nilnil) {
825
+ return Qnil;
826
+ } else {
827
+ rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
828
+ }
829
+ }
830
+ if (rb_block_given_p()) {
831
+ pi->proc = Qnil;
832
+ } else {
833
+ pi->proc = Qundef;
834
+ }
835
+ oj_reader_init(&pi->rd, input, fd);
836
+ pi->json = 0; // indicates reader is in use
837
+
838
+ if (Yes == pi->options.circular) {
839
+ pi->circ_array = oj_circ_array_new();
840
+ } else {
841
+ pi->circ_array = 0;
842
+ }
843
+ if (No == pi->options.allow_gc) {
844
+ rb_gc_disable();
845
+ }
846
+ // GC can run at any time. When it runs any Object created by C will be
847
+ // freed. We protect against this by wrapping the value stack in a ruby
848
+ // data object and poviding a mark function for ruby objects on the
849
+ // value stack (while it is in scope).
850
+ wrapped_stack = oj_stack_init(&pi->stack);
851
+ rb_protect(protect_parse, (VALUE)pi, &line);
852
+ result = stack_head_val(&pi->stack);
853
+ DATA_PTR(wrapped_stack) = 0;
854
+ if (No == pi->options.allow_gc) {
855
+ rb_gc_enable();
856
+ }
857
+ if (!err_has(&pi->err)) {
858
+ // If the stack is not empty then the JSON terminated early.
859
+ Val v;
860
+
861
+ if (0 != (v = stack_peek(&pi->stack))) {
862
+ switch (v->next) {
863
+ case NEXT_ARRAY_NEW:
864
+ case NEXT_ARRAY_ELEMENT:
865
+ case NEXT_ARRAY_COMMA:
866
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Array not terminated");
867
+ break;
868
+ case NEXT_HASH_NEW:
869
+ case NEXT_HASH_KEY:
870
+ case NEXT_HASH_COLON:
871
+ case NEXT_HASH_VALUE:
872
+ case NEXT_HASH_COMMA:
873
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Hash/Object not terminated");
874
+ break;
875
+ default:
876
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not terminated");
877
+ }
878
+ }
879
+ }
880
+ // proceed with cleanup
881
+ if (0 != pi->circ_array) {
882
+ oj_circ_array_free(pi->circ_array);
883
+ }
884
+ stack_cleanup(&pi->stack);
885
+ if (0 != fd) {
886
+ close(fd);
887
+ }
888
+ if (0 != line) {
889
+ rb_jump_tag(line);
890
+ }
891
+ if (err_has(&pi->err)) {
892
+ if (Qnil != pi->err_class) {
893
+ pi->err.clas = pi->err_class;
894
+ }
895
+ if (CompatMode == pi->options.mode) {
896
+ // The json gem requires the error message be UTF-8 encoded. In
897
+ // additional the complete JSON source should be returned but that
898
+ // is not possible without stored all the bytes read and reading
899
+ // the remaining bytes on the stream. Both seem like a very bad
900
+ // idea.
901
+ VALUE args[] = { oj_encode(rb_str_new2(pi->err.msg)) };
902
+
903
+ rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
904
+ } else {
905
+ oj_err_raise(&pi->err);
906
+ }
907
+
908
+ oj_err_raise(&pi->err);
909
+ }
910
+ return result;
911
+ }