oj 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }