oj_windows 3.16.15

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