oj 3.10.7

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