oj 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
data/ext/oj/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
+ }