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/parse.c ADDED
@@ -0,0 +1,1014 @@
1
+ /* parse.c
2
+ * Copyright (c) 2013, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <stdlib.h>
7
+ #include <stdio.h>
8
+ #include <string.h>
9
+ #include <unistd.h>
10
+ #include <math.h>
11
+
12
+ #include "oj.h"
13
+ #include "encode.h"
14
+ #include "parse.h"
15
+ #include "buf.h"
16
+ #include "val_stack.h"
17
+ #include "rxclass.h"
18
+
19
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
20
+ #define OJ_INFINITY (1.0/0.0)
21
+
22
+ //#define EXP_MAX 1023
23
+ #define EXP_MAX 100000
24
+ #define DEC_MAX 15
25
+
26
+ static void
27
+ next_non_white(ParseInfo pi) {
28
+ for (; 1; pi->cur++) {
29
+ switch(*pi->cur) {
30
+ case ' ':
31
+ case '\t':
32
+ case '\f':
33
+ case '\n':
34
+ case '\r':
35
+ break;
36
+ default:
37
+ return;
38
+ }
39
+ }
40
+ }
41
+
42
+ static void
43
+ skip_comment(ParseInfo pi) {
44
+ if ('*' == *pi->cur) {
45
+ pi->cur++;
46
+ for (; pi->cur < pi->end; pi->cur++) {
47
+ if ('*' == *pi->cur && '/' == *(pi->cur + 1)) {
48
+ pi->cur += 2;
49
+ return;
50
+ } else if (pi->end <= pi->cur) {
51
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
52
+ return;
53
+ }
54
+ }
55
+ } else if ('/' == *pi->cur) {
56
+ for (; 1; pi->cur++) {
57
+ switch (*pi->cur) {
58
+ case '\n':
59
+ case '\r':
60
+ case '\f':
61
+ case '\0':
62
+ return;
63
+ default:
64
+ break;
65
+ }
66
+ }
67
+ } else {
68
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
69
+ }
70
+ }
71
+
72
+ static void
73
+ add_value(ParseInfo pi, VALUE rval) {
74
+ Val parent = stack_peek(&pi->stack);
75
+
76
+ if (0 == parent) { // simple add
77
+ pi->add_value(pi, rval);
78
+ } else {
79
+ switch (parent->next) {
80
+ case NEXT_ARRAY_NEW:
81
+ case NEXT_ARRAY_ELEMENT:
82
+ pi->array_append_value(pi, rval);
83
+ parent->next = NEXT_ARRAY_COMMA;
84
+ break;
85
+ case NEXT_HASH_VALUE:
86
+ pi->hash_set_value(pi, parent, rval);
87
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
88
+ xfree((char*)parent->key);
89
+ parent->key = 0;
90
+ }
91
+ parent->next = NEXT_HASH_COMMA;
92
+ break;
93
+ case NEXT_HASH_NEW:
94
+ case NEXT_HASH_KEY:
95
+ case NEXT_HASH_COMMA:
96
+ case NEXT_NONE:
97
+ case NEXT_ARRAY_COMMA:
98
+ case NEXT_HASH_COLON:
99
+ default:
100
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
101
+ break;
102
+ }
103
+ }
104
+ }
105
+
106
+ static void
107
+ read_null(ParseInfo pi) {
108
+ if ('u' == *pi->cur++ && 'l' == *pi->cur++ && 'l' == *pi->cur++) {
109
+ add_value(pi, Qnil);
110
+ } else {
111
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
112
+ }
113
+ }
114
+
115
+ static void
116
+ read_true(ParseInfo pi) {
117
+ if ('r' == *pi->cur++ && 'u' == *pi->cur++ && 'e' == *pi->cur++) {
118
+ add_value(pi, Qtrue);
119
+ } else {
120
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
121
+ }
122
+ }
123
+
124
+ static void
125
+ read_false(ParseInfo pi) {
126
+ if ('a' == *pi->cur++ && 'l' == *pi->cur++ && 's' == *pi->cur++ && 'e' == *pi->cur++) {
127
+ add_value(pi, Qfalse);
128
+ } else {
129
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
130
+ }
131
+ }
132
+
133
+ static uint32_t
134
+ read_hex(ParseInfo pi, const char *h) {
135
+ uint32_t b = 0;
136
+ int i;
137
+
138
+ for (i = 0; i < 4; i++, h++) {
139
+ b = b << 4;
140
+ if ('0' <= *h && *h <= '9') {
141
+ b += *h - '0';
142
+ } else if ('A' <= *h && *h <= 'F') {
143
+ b += *h - 'A' + 10;
144
+ } else if ('a' <= *h && *h <= 'f') {
145
+ b += *h - 'a' + 10;
146
+ } else {
147
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
148
+ return 0;
149
+ }
150
+ }
151
+ return b;
152
+ }
153
+
154
+ static void
155
+ unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
156
+ if (0x0000007F >= code) {
157
+ buf_append(buf, (char)code);
158
+ } else if (0x000007FF >= code) {
159
+ buf_append(buf, 0xC0 | (code >> 6));
160
+ buf_append(buf, 0x80 | (0x3F & code));
161
+ } else if (0x0000FFFF >= code) {
162
+ buf_append(buf, 0xE0 | (code >> 12));
163
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
164
+ buf_append(buf, 0x80 | (0x3F & code));
165
+ } else if (0x001FFFFF >= code) {
166
+ buf_append(buf, 0xF0 | (code >> 18));
167
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
168
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
169
+ buf_append(buf, 0x80 | (0x3F & code));
170
+ } else if (0x03FFFFFF >= code) {
171
+ buf_append(buf, 0xF8 | (code >> 24));
172
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
173
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
174
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
175
+ buf_append(buf, 0x80 | (0x3F & code));
176
+ } else if (0x7FFFFFFF >= code) {
177
+ buf_append(buf, 0xFC | (code >> 30));
178
+ buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
179
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
180
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
181
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
182
+ buf_append(buf, 0x80 | (0x3F & code));
183
+ } else {
184
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
185
+ }
186
+ }
187
+
188
+ // entered at /
189
+ static void
190
+ read_escaped_str(ParseInfo pi, const char *start) {
191
+ struct _Buf buf;
192
+ const char *s;
193
+ int cnt = (int)(pi->cur - start);
194
+ uint32_t code;
195
+ Val parent = stack_peek(&pi->stack);
196
+
197
+ buf_init(&buf);
198
+ if (0 < cnt) {
199
+ buf_append_string(&buf, start, cnt);
200
+ }
201
+ for (s = pi->cur; '"' != *s; s++) {
202
+ if (s >= pi->end) {
203
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
204
+ buf_cleanup(&buf);
205
+ return;
206
+ } else if ('\\' == *s) {
207
+ s++;
208
+ switch (*s) {
209
+ case 'n': buf_append(&buf, '\n'); break;
210
+ case 'r': buf_append(&buf, '\r'); break;
211
+ case 't': buf_append(&buf, '\t'); break;
212
+ case 'f': buf_append(&buf, '\f'); break;
213
+ case 'b': buf_append(&buf, '\b'); break;
214
+ case '"': buf_append(&buf, '"'); break;
215
+ case '/': buf_append(&buf, '/'); break;
216
+ case '\\': buf_append(&buf, '\\'); break;
217
+ case '\'':
218
+ // The json gem claims this is not an error despite the
219
+ // ECMA-404 indicating it is not valid.
220
+ if (CompatMode == pi->options.mode) {
221
+ buf_append(&buf, '\'');
222
+ } else {
223
+ pi->cur = s;
224
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
225
+ buf_cleanup(&buf);
226
+ return;
227
+ }
228
+ break;
229
+ case 'u':
230
+ s++;
231
+ if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
232
+ buf_cleanup(&buf);
233
+ return;
234
+ }
235
+ s += 3;
236
+ if (0x0000D800 <= code && code <= 0x0000DFFF) {
237
+ uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
238
+ uint32_t c2;
239
+
240
+ s++;
241
+ if ('\\' != *s || 'u' != *(s + 1)) {
242
+ if (Yes == pi->options.allow_invalid) {
243
+ s--;
244
+ unicode_to_chars(pi, &buf, code);
245
+ break;
246
+ }
247
+ pi->cur = s;
248
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
249
+ buf_cleanup(&buf);
250
+ return;
251
+ }
252
+ s += 2;
253
+ if (0 == (c2 = read_hex(pi, s)) && err_has(&pi->err)) {
254
+ buf_cleanup(&buf);
255
+ return;
256
+ }
257
+ s += 3;
258
+ c2 = (c2 - 0x0000DC00) & 0x000003FF;
259
+ code = ((c1 << 10) | c2) + 0x00010000;
260
+ }
261
+ unicode_to_chars(pi, &buf, code);
262
+ if (err_has(&pi->err)) {
263
+ buf_cleanup(&buf);
264
+ return;
265
+ }
266
+ break;
267
+ default:
268
+ pi->cur = s;
269
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
270
+ buf_cleanup(&buf);
271
+ return;
272
+ }
273
+ } else {
274
+ buf_append(&buf, *s);
275
+ }
276
+ }
277
+ if (0 == parent) {
278
+ pi->add_cstr(pi, buf.head, buf_len(&buf), start);
279
+ } else {
280
+ switch (parent->next) {
281
+ case NEXT_ARRAY_NEW:
282
+ case NEXT_ARRAY_ELEMENT:
283
+ pi->array_append_cstr(pi, buf.head, buf_len(&buf), start);
284
+ parent->next = NEXT_ARRAY_COMMA;
285
+ break;
286
+ case NEXT_HASH_NEW:
287
+ case NEXT_HASH_KEY:
288
+ if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
289
+ parent->key = strdup(buf.head);
290
+ parent->klen = buf_len(&buf);
291
+ } else {
292
+ parent->key = "";
293
+ parent->klen = 0;
294
+ }
295
+ parent->k1 = *start;
296
+ parent->next = NEXT_HASH_COLON;
297
+ break;
298
+ case NEXT_HASH_VALUE:
299
+ pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), start);
300
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
301
+ xfree((char*)parent->key);
302
+ parent->key = 0;
303
+ }
304
+ parent->next = NEXT_HASH_COMMA;
305
+ break;
306
+ case NEXT_HASH_COMMA:
307
+ case NEXT_NONE:
308
+ case NEXT_ARRAY_COMMA:
309
+ case NEXT_HASH_COLON:
310
+ default:
311
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
312
+ break;
313
+ }
314
+ }
315
+ pi->cur = s + 1;
316
+ buf_cleanup(&buf);
317
+ }
318
+
319
+ static void
320
+ read_str(ParseInfo pi) {
321
+ const char *str = pi->cur;
322
+ Val parent = stack_peek(&pi->stack);
323
+
324
+ for (; '"' != *pi->cur; pi->cur++) {
325
+ if (pi->end <= pi->cur) {
326
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
327
+ return;
328
+ } else if ('\0' == *pi->cur) {
329
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
330
+ return;
331
+ } else if ('\\' == *pi->cur) {
332
+ read_escaped_str(pi, str);
333
+ return;
334
+ }
335
+ }
336
+ if (0 == parent) { // simple add
337
+ pi->add_cstr(pi, str, pi->cur - str, str);
338
+ } else {
339
+ switch (parent->next) {
340
+ case NEXT_ARRAY_NEW:
341
+ case NEXT_ARRAY_ELEMENT:
342
+ pi->array_append_cstr(pi, str, pi->cur - str, str);
343
+ parent->next = NEXT_ARRAY_COMMA;
344
+ break;
345
+ case NEXT_HASH_NEW:
346
+ case NEXT_HASH_KEY:
347
+ if (Qundef == (parent->key_val = pi->hash_key(pi, str, pi->cur - str))) {
348
+ parent->key = str;
349
+ parent->klen = pi->cur - str;
350
+ } else {
351
+ parent->key = "";
352
+ parent->klen = 0;
353
+ }
354
+ parent->k1 = *str;
355
+ parent->next = NEXT_HASH_COLON;
356
+ break;
357
+ case NEXT_HASH_VALUE:
358
+ pi->hash_set_cstr(pi, parent, str, pi->cur - str, str);
359
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
360
+ xfree((char*)parent->key);
361
+ parent->key = 0;
362
+ }
363
+ parent->next = NEXT_HASH_COMMA;
364
+ break;
365
+ case NEXT_HASH_COMMA:
366
+ case NEXT_NONE:
367
+ case NEXT_ARRAY_COMMA:
368
+ case NEXT_HASH_COLON:
369
+ default:
370
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
371
+ break;
372
+ }
373
+ }
374
+ pi->cur++; // move past "
375
+ }
376
+
377
+ static void
378
+ read_num(ParseInfo pi) {
379
+ struct _NumInfo ni;
380
+ Val parent = stack_peek(&pi->stack);
381
+
382
+ ni.str = pi->cur;
383
+ ni.i = 0;
384
+ ni.num = 0;
385
+ ni.div = 1;
386
+ ni.di = 0;
387
+ ni.len = 0;
388
+ ni.exp = 0;
389
+ ni.big = 0;
390
+ ni.infinity = 0;
391
+ ni.nan = 0;
392
+ ni.neg = 0;
393
+ ni.hasExp = 0;
394
+ ni.no_big = (FloatDec == pi->options.bigdec_load);
395
+
396
+ if ('-' == *pi->cur) {
397
+ pi->cur++;
398
+ ni.neg = 1;
399
+ } else if ('+' == *pi->cur) {
400
+ pi->cur++;
401
+ }
402
+ if ('I' == *pi->cur) {
403
+ if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
404
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
405
+ return;
406
+ }
407
+ pi->cur += 8;
408
+ ni.infinity = 1;
409
+ } else if ('N' == *pi->cur || 'n' == *pi->cur) {
410
+ if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
411
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
412
+ return;
413
+ }
414
+ pi->cur += 3;
415
+ ni.nan = 1;
416
+ } else {
417
+ int dec_cnt = 0;
418
+ bool zero1 = false;
419
+
420
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
421
+ if (0 == ni.i && '0' == *pi->cur) {
422
+ zero1 = true;
423
+ }
424
+ if (0 < ni.i) {
425
+ dec_cnt++;
426
+ }
427
+ if (!ni.big) {
428
+ int d = (*pi->cur - '0');
429
+
430
+ if (0 < d) {
431
+ if (zero1 && CompatMode == pi->options.mode) {
432
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
433
+ return;
434
+ }
435
+ zero1 = false;
436
+ }
437
+ ni.i = ni.i * 10 + d;
438
+ if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
439
+ ni.big = 1;
440
+ }
441
+ }
442
+ }
443
+ if ('.' == *pi->cur) {
444
+ pi->cur++;
445
+ if (*pi->cur < '0' || '9' < *pi->cur) {
446
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
447
+ return;
448
+ }
449
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
450
+ int d = (*pi->cur - '0');
451
+
452
+ if (0 < ni.num || 0 < ni.i) {
453
+ dec_cnt++;
454
+ }
455
+ ni.num = ni.num * 10 + d;
456
+ ni.div *= 10;
457
+ ni.di++;
458
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
459
+ ni.big = 1;
460
+ }
461
+ }
462
+ }
463
+ if ('e' == *pi->cur || 'E' == *pi->cur) {
464
+ int eneg = 0;
465
+
466
+ ni.hasExp = 1;
467
+ pi->cur++;
468
+ if ('-' == *pi->cur) {
469
+ pi->cur++;
470
+ eneg = 1;
471
+ } else if ('+' == *pi->cur) {
472
+ pi->cur++;
473
+ }
474
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
475
+ ni.exp = ni.exp * 10 + (*pi->cur - '0');
476
+ if (EXP_MAX <= ni.exp) {
477
+ ni.big = 1;
478
+ }
479
+ }
480
+ if (eneg) {
481
+ ni.exp = -ni.exp;
482
+ }
483
+ }
484
+ ni.len = pi->cur - ni.str;
485
+ }
486
+ // Check for special reserved values for Infinity and NaN.
487
+ if (ni.big) {
488
+ if (0 == strcasecmp(INF_VAL, ni.str)) {
489
+ ni.infinity = 1;
490
+ } else if (0 == strcasecmp(NINF_VAL, ni.str)) {
491
+ ni.infinity = 1;
492
+ ni.neg = 1;
493
+ } else if (0 == strcasecmp(NAN_VAL, ni.str)) {
494
+ ni.nan = 1;
495
+ }
496
+ }
497
+ if (BigDec == pi->options.bigdec_load) {
498
+ ni.big = 1;
499
+ }
500
+ if (0 == parent) {
501
+ pi->add_num(pi, &ni);
502
+ } else {
503
+ switch (parent->next) {
504
+ case NEXT_ARRAY_NEW:
505
+ case NEXT_ARRAY_ELEMENT:
506
+ pi->array_append_num(pi, &ni);
507
+ parent->next = NEXT_ARRAY_COMMA;
508
+ break;
509
+ case NEXT_HASH_VALUE:
510
+ pi->hash_set_num(pi, parent, &ni);
511
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
512
+ xfree((char*)parent->key);
513
+ parent->key = 0;
514
+ }
515
+ parent->next = NEXT_HASH_COMMA;
516
+ break;
517
+ default:
518
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
519
+ break;
520
+ }
521
+ }
522
+ }
523
+
524
+ static void
525
+ array_start(ParseInfo pi) {
526
+ volatile VALUE v = pi->start_array(pi);
527
+
528
+ stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
529
+ }
530
+
531
+ static void
532
+ array_end(ParseInfo pi) {
533
+ Val array = stack_pop(&pi->stack);
534
+
535
+ if (0 == array) {
536
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
537
+ } else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
538
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not an array close", oj_stack_next_string(array->next));
539
+ } else {
540
+ pi->end_array(pi);
541
+ add_value(pi, array->val);
542
+ }
543
+ }
544
+
545
+ static void
546
+ hash_start(ParseInfo pi) {
547
+ volatile VALUE v = pi->start_hash(pi);
548
+
549
+ stack_push(&pi->stack, v, NEXT_HASH_NEW);
550
+ }
551
+
552
+ static void
553
+ hash_end(ParseInfo pi) {
554
+ volatile Val hash = stack_peek(&pi->stack);
555
+
556
+ // leave hash on stack until just before
557
+ if (0 == hash) {
558
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
559
+ } else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
560
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a hash close", oj_stack_next_string(hash->next));
561
+ } else {
562
+ pi->end_hash(pi);
563
+ stack_pop(&pi->stack);
564
+ add_value(pi, hash->val);
565
+ }
566
+ }
567
+
568
+ static void
569
+ comma(ParseInfo pi) {
570
+ Val parent = stack_peek(&pi->stack);
571
+
572
+ if (0 == parent) {
573
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
574
+ } else if (NEXT_ARRAY_COMMA == parent->next) {
575
+ parent->next = NEXT_ARRAY_ELEMENT;
576
+ } else if (NEXT_HASH_COMMA == parent->next) {
577
+ parent->next = NEXT_HASH_KEY;
578
+ } else {
579
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
580
+ }
581
+ }
582
+
583
+ static void
584
+ colon(ParseInfo pi) {
585
+ Val parent = stack_peek(&pi->stack);
586
+
587
+ if (0 != parent && NEXT_HASH_COLON == parent->next) {
588
+ parent->next = NEXT_HASH_VALUE;
589
+ } else {
590
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
591
+ }
592
+ }
593
+
594
+ void
595
+ oj_parse2(ParseInfo pi) {
596
+ int first = 1;
597
+ long start = 0;
598
+
599
+ pi->cur = pi->json;
600
+ err_init(&pi->err);
601
+ while (1) {
602
+ if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
603
+ VALUE err_clas = oj_get_json_err_class("NestingError");
604
+
605
+ oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
606
+ pi->err_class = err_clas;
607
+ return;
608
+ }
609
+ next_non_white(pi);
610
+ if (!first && '\0' != *pi->cur) {
611
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
612
+ }
613
+
614
+ // If no tokens are consumed (i.e. empty string), throw a parse error
615
+ // this is the behavior of JSON.parse in both Ruby and JS.
616
+ if (No == pi->options.empty_string && 1 == first && '\0' == *pi->cur) {
617
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
618
+ }
619
+
620
+ switch (*pi->cur++) {
621
+ case '{':
622
+ hash_start(pi);
623
+ break;
624
+ case '}':
625
+ hash_end(pi);
626
+ break;
627
+ case ':':
628
+ colon(pi);
629
+ break;
630
+ case '[':
631
+ array_start(pi);
632
+ break;
633
+ case ']':
634
+ array_end(pi);
635
+ break;
636
+ case ',':
637
+ comma(pi);
638
+ break;
639
+ case '"':
640
+ read_str(pi);
641
+ break;
642
+ //case '+':
643
+ case '-':
644
+ case '0':
645
+ case '1':
646
+ case '2':
647
+ case '3':
648
+ case '4':
649
+ case '5':
650
+ case '6':
651
+ case '7':
652
+ case '8':
653
+ case '9':
654
+ pi->cur--;
655
+ read_num(pi);
656
+ break;
657
+ case 'I':
658
+ case 'N':
659
+ if (Yes == pi->options.allow_nan) {
660
+ pi->cur--;
661
+ read_num(pi);
662
+ } else {
663
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
664
+ }
665
+ break;
666
+ case 't':
667
+ read_true(pi);
668
+ break;
669
+ case 'f':
670
+ read_false(pi);
671
+ break;
672
+ case 'n':
673
+ if ('u' == *pi->cur) {
674
+ read_null(pi);
675
+ } else {
676
+ pi->cur--;
677
+ read_num(pi);
678
+ }
679
+ break;
680
+ case '/':
681
+ skip_comment(pi);
682
+ if (first) {
683
+ continue;
684
+ }
685
+ break;
686
+ case '\0':
687
+ pi->cur--;
688
+ return;
689
+ default:
690
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
691
+ return;
692
+ }
693
+ if (err_has(&pi->err)) {
694
+ return;
695
+ }
696
+ if (stack_empty(&pi->stack)) {
697
+ if (Qundef != pi->proc) {
698
+ VALUE args[3];
699
+ long len = (pi->cur - pi->json) - start;
700
+
701
+ *args = stack_head_val(&pi->stack);
702
+ args[1] = LONG2NUM(start);
703
+ args[2] = LONG2NUM(len);
704
+
705
+ if (Qnil == pi->proc) {
706
+ rb_yield_values2(3, args);
707
+ } else {
708
+ #if HAS_PROC_WITH_BLOCK
709
+ rb_proc_call_with_block(pi->proc, 3, args, Qnil);
710
+ #else
711
+ rb_raise(rb_eNotImpError,
712
+ "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
713
+ #endif
714
+ }
715
+ } else if (!pi->has_callbacks) {
716
+ first = 0;
717
+ }
718
+ start = pi->cur - pi->json;
719
+ }
720
+ }
721
+ }
722
+
723
+ VALUE
724
+ oj_num_as_value(NumInfo ni) {
725
+ volatile VALUE rnum = Qnil;
726
+
727
+ if (ni->infinity) {
728
+ if (ni->neg) {
729
+ rnum = rb_float_new(-OJ_INFINITY);
730
+ } else {
731
+ rnum = rb_float_new(OJ_INFINITY);
732
+ }
733
+ } else if (ni->nan) {
734
+ rnum = rb_float_new(0.0/0.0);
735
+ } else if (1 == ni->div && 0 == ni->exp) { // fixnum
736
+ if (ni->big) {
737
+ if (256 > ni->len) {
738
+ char buf[256];
739
+
740
+ memcpy(buf, ni->str, ni->len);
741
+ buf[ni->len] = '\0';
742
+ rnum = rb_cstr_to_inum(buf, 10, 0);
743
+ } else {
744
+ char *buf = ALLOC_N(char, ni->len + 1);
745
+
746
+ memcpy(buf, ni->str, ni->len);
747
+ buf[ni->len] = '\0';
748
+ rnum = rb_cstr_to_inum(buf, 10, 0);
749
+ xfree(buf);
750
+ }
751
+ } else {
752
+ if (ni->neg) {
753
+ rnum = rb_ll2inum(-ni->i);
754
+ } else {
755
+ rnum = rb_ll2inum(ni->i);
756
+ }
757
+ }
758
+ } else { // decimal
759
+ if (ni->big) {
760
+ rnum = rb_funcall(oj_bigdecimal_class, oj_new_id, 1, rb_str_new(ni->str, ni->len));
761
+ if (ni->no_big) {
762
+ rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
763
+ }
764
+ } else {
765
+ // All these machinations are to get rounding to work better.
766
+ long double d = (long double)ni->i * (long double)ni->div + (long double)ni->num;
767
+ int x = ni->exp - ni->di;
768
+
769
+ // Rounding sometimes cuts off the last digit even if there are only
770
+ // 15 digits. This attempts to fix those few cases where this
771
+ // occurs.
772
+ if ((long double)INT64_MAX > d && (int64_t)d != (ni->i * ni->div + ni->num)) {
773
+ rnum = rb_funcall(oj_bigdecimal_class, oj_new_id, 1, rb_str_new(ni->str, ni->len));
774
+ if (ni->no_big) {
775
+ rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
776
+ }
777
+ } else {
778
+ d = roundl(d);
779
+ if (0 < x) {
780
+ d *= powl(10.0L, x);
781
+ } else if (0 > x) {
782
+ d /= powl(10.0L, -x);
783
+ }
784
+ if (ni->neg) {
785
+ d = -d;
786
+ }
787
+ rnum = rb_float_new((double)d);
788
+ }
789
+ }
790
+ }
791
+ return rnum;
792
+ }
793
+
794
+ void
795
+ oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const char *format, ...) {
796
+ va_list ap;
797
+ char msg[128];
798
+
799
+ va_start(ap, format);
800
+ vsnprintf(msg, sizeof(msg) - 1, format, ap);
801
+ va_end(ap);
802
+ pi->err.clas = err_clas;
803
+ if (0 == pi->json) {
804
+ oj_err_set(&pi->err, err_clas, "%s at line %d, column %d [%s:%d]", msg, pi->rd.line, pi->rd.col, file, line);
805
+ } else {
806
+ _oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
807
+ }
808
+ }
809
+
810
+ static VALUE
811
+ protect_parse(VALUE pip) {
812
+ oj_parse2((ParseInfo)pip);
813
+
814
+ return Qnil;
815
+ }
816
+
817
+ extern int oj_utf8_index;
818
+
819
+ static void
820
+ oj_pi_set_input_str(ParseInfo pi, volatile VALUE *inputp) {
821
+ #if HAS_ENCODING_SUPPORT
822
+ rb_encoding *enc = rb_to_encoding(rb_obj_encoding(*inputp));
823
+
824
+ if (rb_utf8_encoding() != enc) {
825
+ *inputp = rb_str_conv_enc(*inputp, enc, rb_utf8_encoding());
826
+ }
827
+ #endif
828
+ pi->json = rb_string_value_ptr((VALUE*)inputp);
829
+ pi->end = pi->json + RSTRING_LEN(*inputp);
830
+ }
831
+
832
+ VALUE
833
+ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
834
+ char *buf = 0;
835
+ volatile VALUE input;
836
+ volatile VALUE wrapped_stack;
837
+ volatile VALUE result = Qnil;
838
+ int line = 0;
839
+ int free_json = 0;
840
+
841
+ if (argc < 1) {
842
+ rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
843
+ }
844
+ input = argv[0];
845
+ if (2 <= argc) {
846
+ if (T_HASH == rb_type(argv[1])) {
847
+ oj_parse_options(argv[1], &pi->options);
848
+ } else if (3 <= argc && T_HASH == rb_type(argv[2])) {
849
+ oj_parse_options(argv[2], &pi->options);
850
+ }
851
+ }
852
+ if (yieldOk && rb_block_given_p()) {
853
+ pi->proc = Qnil;
854
+ } else {
855
+ pi->proc = Qundef;
856
+ }
857
+ if (0 != json) {
858
+ pi->json = json;
859
+ pi->end = json + len;
860
+ free_json = 1;
861
+ } else if (T_STRING == rb_type(input)) {
862
+ if (No == pi->options.nilnil && 0 == RSTRING_LEN(input)) {
863
+ rb_raise(oj_json_parser_error_class, "An empty string is not a valid JSON string.");
864
+ }
865
+ oj_pi_set_input_str(pi, &input);
866
+ } else if (Qnil == input) {
867
+ if (Yes == pi->options.nilnil) {
868
+ return Qnil;
869
+ } else {
870
+ rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
871
+ }
872
+ } else {
873
+ VALUE clas = rb_obj_class(input);
874
+ volatile VALUE s;
875
+
876
+ if (oj_stringio_class == clas) {
877
+ s = rb_funcall2(input, oj_string_id, 0, 0);
878
+ oj_pi_set_input_str(pi, &s);
879
+ #if !IS_WINDOWS
880
+ } else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
881
+ int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
882
+ ssize_t cnt;
883
+ size_t len = lseek(fd, 0, SEEK_END);
884
+
885
+ lseek(fd, 0, SEEK_SET);
886
+ buf = ALLOC_N(char, len + 1);
887
+ pi->json = buf;
888
+ pi->end = buf + len;
889
+ if (0 >= (cnt = read(fd, (char*)pi->json, len)) || cnt != (ssize_t)len) {
890
+ if (0 != buf) {
891
+ xfree(buf);
892
+ }
893
+ rb_raise(rb_eIOError, "failed to read from IO Object.");
894
+ }
895
+ ((char*)pi->json)[len] = '\0';
896
+ /* skip UTF-8 BOM if present */
897
+ if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
898
+ pi->cur += 3;
899
+ }
900
+ #endif
901
+ } else if (rb_respond_to(input, oj_read_id)) {
902
+ // use stream parser instead
903
+ return oj_pi_sparse(argc, argv, pi, 0);
904
+ } else {
905
+ rb_raise(rb_eArgError, "parse() expected a String or IO Object.");
906
+ }
907
+ }
908
+ if (Yes == pi->options.circular) {
909
+ pi->circ_array = oj_circ_array_new();
910
+ } else {
911
+ pi->circ_array = 0;
912
+ }
913
+ if (No == pi->options.allow_gc) {
914
+ rb_gc_disable();
915
+ }
916
+ // GC can run at any time. When it runs any Object created by C will be
917
+ // freed. We protect against this by wrapping the value stack in a ruby
918
+ // data object and poviding a mark function for ruby objects on the
919
+ // value stack (while it is in scope).
920
+ wrapped_stack = oj_stack_init(&pi->stack);
921
+ rb_protect(protect_parse, (VALUE)pi, &line);
922
+ result = stack_head_val(&pi->stack);
923
+ DATA_PTR(wrapped_stack) = 0;
924
+ if (No == pi->options.allow_gc) {
925
+ rb_gc_enable();
926
+ }
927
+ if (!err_has(&pi->err)) {
928
+ // If the stack is not empty then the JSON terminated early.
929
+ Val v;
930
+
931
+ if (0 != (v = stack_peek(&pi->stack))) {
932
+ switch (v->next) {
933
+ case NEXT_ARRAY_NEW:
934
+ case NEXT_ARRAY_ELEMENT:
935
+ case NEXT_ARRAY_COMMA:
936
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Array not terminated");
937
+ break;
938
+ case NEXT_HASH_NEW:
939
+ case NEXT_HASH_KEY:
940
+ case NEXT_HASH_COLON:
941
+ case NEXT_HASH_VALUE:
942
+ case NEXT_HASH_COMMA:
943
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Hash/Object not terminated");
944
+ break;
945
+ default:
946
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not terminated");
947
+ }
948
+ }
949
+ }
950
+ // proceed with cleanup
951
+ if (0 != pi->circ_array) {
952
+ oj_circ_array_free(pi->circ_array);
953
+ }
954
+ if (0 != buf) {
955
+ xfree(buf);
956
+ } else if (free_json) {
957
+ xfree(json);
958
+ }
959
+ stack_cleanup(&pi->stack);
960
+ if (pi->str_rx.head != oj_default_options.str_rx.head) {
961
+ oj_rxclass_cleanup(&pi->str_rx);
962
+ }
963
+ if (0 != line) {
964
+ rb_jump_tag(line);
965
+ }
966
+ if (err_has(&pi->err)) {
967
+ if (Qnil != pi->err_class) {
968
+ pi->err.clas = pi->err_class;
969
+ }
970
+ if (CompatMode == pi->options.mode) {
971
+ // The json gem requires the error message be UTF-8 encoded. In
972
+ // additional the complete JSON source must be returned. There
973
+ // does not seem to be a size limit.
974
+ VALUE msg = oj_encode(rb_str_new2(pi->err.msg));
975
+ VALUE args[1];
976
+
977
+ if (NULL != pi->json) {
978
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(" in '")));
979
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
980
+ }
981
+ args[0] = msg;
982
+ rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
983
+ } else {
984
+ oj_err_raise(&pi->err);
985
+ }
986
+ }
987
+ if (pi->options.quirks_mode == No) {
988
+ switch (rb_type(result)) {
989
+ case T_NIL:
990
+ case T_TRUE:
991
+ case T_FALSE:
992
+ case T_FIXNUM:
993
+ case T_FLOAT:
994
+ case T_CLASS:
995
+ case T_STRING:
996
+ case T_SYMBOL: {
997
+ struct _Err err;
998
+
999
+ if (Qnil == pi->err_class) {
1000
+ err.clas = oj_parse_error_class;
1001
+ } else {
1002
+ err.clas = pi->err_class;
1003
+ }
1004
+ snprintf(err.msg, sizeof(err.msg), "unexpected non-document value");
1005
+ oj_err_raise(&err);
1006
+ break;
1007
+ }
1008
+ default:
1009
+ // okay
1010
+ break;
1011
+ }
1012
+ }
1013
+ return result;
1014
+ }