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,1218 @@
1
+ /* custom.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <stdint.h>
7
+ #include <stdio.h>
8
+
9
+ #include "code.h"
10
+ #include "dump.h"
11
+ #include "encode.h"
12
+ #include "err.h"
13
+ #include "hash.h"
14
+ #include "odd.h"
15
+ #include "oj.h"
16
+ #include "parse.h"
17
+ #include "resolve.h"
18
+ #include "trace.h"
19
+ #include "util.h"
20
+
21
+ extern void oj_set_obj_ivar(Val parent, Val kval, VALUE value);
22
+ extern VALUE oj_parse_xml_time(const char *str, int len); // from object.c
23
+
24
+ static void
25
+ dump_obj_str(VALUE obj, int depth, Out out) {
26
+ struct _attr attrs[] = {
27
+ { "s", 1, Qnil },
28
+ { NULL, 0, Qnil },
29
+ };
30
+ attrs->value = rb_funcall(obj, oj_to_s_id, 0);
31
+
32
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
33
+ }
34
+
35
+ static void
36
+ dump_obj_as_str(VALUE obj, int depth, Out out) {
37
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
38
+ const char *str = rb_string_value_ptr((VALUE*)&rstr);
39
+
40
+ oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
41
+ }
42
+
43
+ static void
44
+ bigdecimal_dump(VALUE obj, int depth, Out out) {
45
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
46
+ const char *str = rb_string_value_ptr((VALUE*)&rstr);
47
+ int len = (int)RSTRING_LEN(rstr);
48
+
49
+ if (0 == strcasecmp("Infinity", str)) {
50
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
51
+ oj_dump_raw(str, len, out);
52
+ } else if (0 == strcasecmp("-Infinity", str)) {
53
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
54
+ oj_dump_raw(str, len, out);
55
+ } else if (No == out->opts->bigdec_as_num) {
56
+ oj_dump_cstr(str, len, 0, 0, out);
57
+ } else {
58
+ oj_dump_raw(str, len, out);
59
+ }
60
+ }
61
+
62
+ static ID real_id = 0;
63
+ static ID imag_id = 0;
64
+
65
+ static void
66
+ complex_dump(VALUE obj, int depth, Out out) {
67
+ if (NULL != out->opts->create_id) {
68
+ struct _attr attrs[] = {
69
+ { "real", 4, Qnil },
70
+ { "imag", 4, Qnil },
71
+ { NULL, 0, Qnil },
72
+ };
73
+ if (0 == real_id) {
74
+ real_id = rb_intern("real");
75
+ imag_id = rb_intern("imag");
76
+ }
77
+ attrs[0].value = rb_funcall(obj, real_id, 0);
78
+ attrs[1].value = rb_funcall(obj, imag_id, 0);
79
+
80
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
81
+ } else {
82
+ dump_obj_as_str(obj, depth, out);
83
+ }
84
+ }
85
+
86
+ static VALUE
87
+ complex_load(VALUE clas, VALUE args) {
88
+ if (0 == real_id) {
89
+ real_id = rb_intern("real");
90
+ imag_id = rb_intern("imag");
91
+ }
92
+ return rb_complex_new(rb_hash_aref(args, rb_id2str(real_id)), rb_hash_aref(args, rb_id2str(imag_id)));
93
+ }
94
+
95
+ static void
96
+ time_dump(VALUE obj, int depth, Out out) {
97
+ if (Yes == out->opts->create_ok) {
98
+ struct _attr attrs[] = {
99
+ { "time", 4, Qundef, 0, Qundef },
100
+ { NULL, 0, Qnil },
101
+ };
102
+ attrs->time = obj;
103
+
104
+ oj_code_attrs(obj, attrs, depth, out, true);
105
+ } else {
106
+ switch (out->opts->time_format) {
107
+ case RubyTime: oj_dump_ruby_time(obj, out); break;
108
+ case XmlTime: oj_dump_xml_time(obj, out); break;
109
+ case UnixZTime: oj_dump_time(obj, out, true); break;
110
+ case UnixTime:
111
+ default: oj_dump_time(obj, out, false); break;
112
+ }
113
+ }
114
+ }
115
+
116
+ static void
117
+ date_dump(VALUE obj, int depth, Out out) {
118
+ if (Yes == out->opts->create_ok) {
119
+ struct _attr attrs[] = {
120
+ { "s", 1, Qnil },
121
+ { NULL, 0, Qnil },
122
+ };
123
+ attrs->value = rb_funcall(obj, rb_intern("iso8601"), 0);
124
+
125
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
126
+ } else {
127
+ volatile VALUE v;
128
+ volatile VALUE ov;
129
+
130
+ switch (out->opts->time_format) {
131
+ case RubyTime:
132
+ case XmlTime:
133
+ v = rb_funcall(obj, rb_intern("iso8601"), 0);
134
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&v), (int)RSTRING_LEN(v), 0, 0, out);
135
+ break;
136
+ case UnixZTime:
137
+ v = rb_funcall(obj, rb_intern("to_time"), 0);
138
+ if (oj_date_class == rb_obj_class(obj)) {
139
+ ov = rb_funcall(v, rb_intern("utc_offset"), 0);
140
+ v = rb_funcall(v, rb_intern("utc"), 0);
141
+ v = rb_funcall(v, rb_intern("+"), 1, ov);
142
+ oj_dump_time(v, out, false);
143
+ } else {
144
+ oj_dump_time(v, out, true);
145
+ }
146
+ break;
147
+ case UnixTime:
148
+ default:
149
+ v = rb_funcall(obj, rb_intern("to_time"), 0);
150
+ if (oj_date_class == rb_obj_class(obj)) {
151
+ ov = rb_funcall(v, rb_intern("utc_offset"), 0);
152
+ v = rb_funcall(v, rb_intern("utc"), 0);
153
+ v = rb_funcall(v, rb_intern("+"), 1, ov);
154
+ }
155
+ oj_dump_time(v, out, false);
156
+ break;
157
+ }
158
+ }
159
+ }
160
+
161
+ static VALUE
162
+ date_load(VALUE clas, VALUE args) {
163
+ volatile VALUE v;
164
+
165
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
166
+ return rb_funcall(oj_date_class, rb_intern("parse"), 1, v);
167
+ }
168
+ return Qnil;
169
+ }
170
+
171
+ static VALUE
172
+ datetime_load(VALUE clas, VALUE args) {
173
+ volatile VALUE v;
174
+
175
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
176
+ return rb_funcall(oj_datetime_class, rb_intern("parse"), 1, v);
177
+ }
178
+ return Qnil;
179
+ }
180
+
181
+ static ID table_id = 0;
182
+
183
+ static void
184
+ openstruct_dump(VALUE obj, int depth, Out out) {
185
+ struct _attr attrs[] = {
186
+ { "table", 5, Qnil },
187
+ { NULL, 0, Qnil },
188
+ };
189
+ if (0 == table_id) {
190
+ table_id = rb_intern("table");
191
+ }
192
+ attrs->value = rb_funcall(obj, table_id, 0);
193
+
194
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
195
+ }
196
+
197
+ static VALUE
198
+ openstruct_load(VALUE clas, VALUE args) {
199
+ if (0 == table_id) {
200
+ table_id = rb_intern("table");
201
+ }
202
+ return rb_funcall(clas, oj_new_id, 1, rb_hash_aref(args, rb_id2str(table_id)));
203
+ }
204
+
205
+ static void
206
+ range_dump(VALUE obj, int depth, Out out) {
207
+ if (NULL != out->opts->create_id) {
208
+ struct _attr attrs[] = {
209
+ { "begin", 5, Qnil },
210
+ { "end", 3, Qnil },
211
+ { "exclude", 7, Qnil },
212
+ { NULL, 0, Qnil },
213
+ };
214
+ attrs[0].value = rb_funcall(obj, oj_begin_id, 0);
215
+ attrs[1].value = rb_funcall(obj, oj_end_id, 0);
216
+ attrs[2].value = rb_funcall(obj, oj_exclude_end_id, 0);
217
+
218
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
219
+ } else {
220
+ dump_obj_as_str(obj, depth, out);
221
+ }
222
+ }
223
+
224
+ static VALUE
225
+ range_load(VALUE clas, VALUE args) {
226
+ VALUE nargs[3];
227
+
228
+ nargs[0] = rb_hash_aref(args, rb_id2str(oj_begin_id));
229
+ nargs[1] = rb_hash_aref(args, rb_id2str(oj_end_id));
230
+ nargs[2] = rb_hash_aref(args, rb_id2str(oj_exclude_end_id));
231
+
232
+ return rb_class_new_instance(3, nargs, rb_cRange);
233
+ }
234
+
235
+ static ID numerator_id = 0;
236
+ static ID denominator_id = 0;
237
+
238
+ static void
239
+ rational_dump(VALUE obj, int depth, Out out) {
240
+ if (NULL != out->opts->create_id) {
241
+ struct _attr attrs[] = {
242
+ { "numerator", 9, Qnil },
243
+ { "denominator", 11, Qnil },
244
+ { NULL, 0, Qnil },
245
+ };
246
+ if (0 == numerator_id) {
247
+ numerator_id = rb_intern("numerator");
248
+ denominator_id = rb_intern("denominator");
249
+ }
250
+ attrs[0].value = rb_funcall(obj, numerator_id, 0);
251
+ attrs[1].value = rb_funcall(obj, denominator_id, 0);
252
+
253
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
254
+ } else {
255
+ dump_obj_as_str(obj, depth, out);
256
+ }
257
+ }
258
+
259
+ static VALUE
260
+ rational_load(VALUE clas, VALUE args) {
261
+ if (0 == numerator_id) {
262
+ numerator_id = rb_intern("numerator");
263
+ denominator_id = rb_intern("denominator");
264
+ }
265
+ return rb_rational_new(rb_hash_aref(args, rb_id2str(numerator_id)),
266
+ rb_hash_aref(args, rb_id2str(denominator_id)));
267
+ }
268
+
269
+ static VALUE
270
+ regexp_load(VALUE clas, VALUE args) {
271
+ volatile VALUE v;
272
+
273
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
274
+ return rb_funcall(rb_cRegexp, oj_new_id, 1, v);
275
+ }
276
+ return Qnil;
277
+ }
278
+
279
+ static VALUE
280
+ time_load(VALUE clas, VALUE args) {
281
+ // Value should have already been replaced in one of the hash_set_xxx
282
+ // functions.
283
+ return args;
284
+ }
285
+
286
+ static struct _code codes[] = {
287
+ { "BigDecimal", Qnil, bigdecimal_dump, NULL, true },
288
+ { "Complex", Qnil, complex_dump, complex_load, true },
289
+ { "Date", Qnil, date_dump, date_load, true },
290
+ { "DateTime", Qnil, date_dump, datetime_load, true },
291
+ { "OpenStruct", Qnil, openstruct_dump, openstruct_load, true },
292
+ { "Range", Qnil, range_dump, range_load, true },
293
+ { "Rational", Qnil, rational_dump, rational_load, true },
294
+ { "Regexp", Qnil, dump_obj_str, regexp_load, true },
295
+ { "Time", Qnil, time_dump, time_load, true },
296
+ { NULL, Qundef, NULL, NULL, false },
297
+ };
298
+
299
+ static int
300
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
301
+ Out out = (Out)ov;
302
+ int depth = out->depth;
303
+
304
+ if (oj_dump_ignore(out->opts, value)) {
305
+ return ST_CONTINUE;
306
+ }
307
+ if (out->omit_nil && Qnil == value) {
308
+ return ST_CONTINUE;
309
+ }
310
+ if (!out->opts->dump_opts.use) {
311
+ assure_size(out, depth * out->indent + 1);
312
+ fill_indent(out, depth);
313
+ } else {
314
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
315
+ if (0 < out->opts->dump_opts.hash_size) {
316
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
317
+ out->cur += out->opts->dump_opts.hash_size;
318
+ }
319
+ if (0 < out->opts->dump_opts.indent_size) {
320
+ int i;
321
+
322
+ for (i = depth; 0 < i; i--) {
323
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
324
+ out->cur += out->opts->dump_opts.indent_size;
325
+ }
326
+ }
327
+ }
328
+ switch (rb_type(key)) {
329
+ case T_STRING:
330
+ oj_dump_str(key, 0, out, false);
331
+ break;
332
+ case T_SYMBOL:
333
+ oj_dump_sym(key, 0, out, false);
334
+ break;
335
+ default:
336
+ oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false);
337
+ break;
338
+ }
339
+ if (!out->opts->dump_opts.use) {
340
+ *out->cur++ = ':';
341
+ } else {
342
+ assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
343
+ if (0 < out->opts->dump_opts.before_size) {
344
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
345
+ out->cur += out->opts->dump_opts.before_size;
346
+ }
347
+ *out->cur++ = ':';
348
+ if (0 < out->opts->dump_opts.after_size) {
349
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
350
+ out->cur += out->opts->dump_opts.after_size;
351
+ }
352
+ }
353
+ oj_dump_custom_val(value, depth, out, true);
354
+ out->depth = depth;
355
+ *out->cur++ = ',';
356
+
357
+ return ST_CONTINUE;
358
+ }
359
+
360
+ static void
361
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
362
+ int cnt;
363
+ long id = oj_check_circular(obj, out);
364
+
365
+ if (0 > id) {
366
+ oj_dump_nil(Qnil, depth, out, false);
367
+ return;
368
+ }
369
+ cnt = (int)RHASH_SIZE(obj);
370
+ assure_size(out, 2);
371
+ if (0 == cnt) {
372
+ *out->cur++ = '{';
373
+ *out->cur++ = '}';
374
+ } else {
375
+ *out->cur++ = '{';
376
+ out->depth = depth + 1;
377
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
378
+ if (',' == *(out->cur - 1)) {
379
+ out->cur--; // backup to overwrite last comma
380
+ }
381
+ if (!out->opts->dump_opts.use) {
382
+ assure_size(out, depth * out->indent + 2);
383
+ fill_indent(out, depth);
384
+ } else {
385
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
386
+ if (0 < out->opts->dump_opts.hash_size) {
387
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
388
+ out->cur += out->opts->dump_opts.hash_size;
389
+ }
390
+ if (0 < out->opts->dump_opts.indent_size) {
391
+ int i;
392
+
393
+ for (i = depth; 0 < i; i--) {
394
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
395
+ out->cur += out->opts->dump_opts.indent_size;
396
+ }
397
+ }
398
+ }
399
+ *out->cur++ = '}';
400
+ }
401
+ *out->cur = '\0';
402
+ }
403
+
404
+ static void
405
+ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
406
+ ID *idp;
407
+ AttrGetFunc *fp;
408
+ volatile VALUE v;
409
+ const char *name;
410
+ size_t size;
411
+ int d2 = depth + 1;
412
+
413
+ assure_size(out, 2);
414
+ *out->cur++ = '{';
415
+ if (NULL != out->opts->create_id && Yes == out->opts->create_ok) {
416
+ const char *classname = rb_class2name(clas);
417
+ int clen = (int)strlen(classname);
418
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
419
+
420
+ size = d2 * out->indent + 10 + clen + out->opts->create_id_len + sep_len;
421
+ assure_size(out, size);
422
+ fill_indent(out, d2);
423
+ *out->cur++ = '"';
424
+ memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
425
+ out->cur += out->opts->create_id_len;
426
+ *out->cur++ = '"';
427
+ if (0 < out->opts->dump_opts.before_size) {
428
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
429
+ out->cur += out->opts->dump_opts.before_size;
430
+ }
431
+ *out->cur++ = ':';
432
+ if (0 < out->opts->dump_opts.after_size) {
433
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
434
+ out->cur += out->opts->dump_opts.after_size;
435
+ }
436
+ *out->cur++ = '"';
437
+ memcpy(out->cur, classname, clen);
438
+ out->cur += clen;
439
+ *out->cur++ = '"';
440
+ *out->cur++ = ',';
441
+ }
442
+ if (odd->raw) {
443
+ v = rb_funcall(obj, *odd->attrs, 0);
444
+ if (Qundef == v || T_STRING != rb_type(v)) {
445
+ rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
446
+ } else {
447
+ const char *s = rb_string_value_ptr((VALUE*)&v);
448
+ int len = (int)RSTRING_LEN(v);
449
+ const char *name = rb_id2name(*odd->attrs);
450
+ size_t nlen = strlen(name);
451
+
452
+ size = len + d2 * out->indent + nlen + 10;
453
+ assure_size(out, size);
454
+ fill_indent(out, d2);
455
+ *out->cur++ = '"';
456
+ memcpy(out->cur, name, nlen);
457
+ out->cur += nlen;
458
+ *out->cur++ = '"';
459
+ *out->cur++ = ':';
460
+ memcpy(out->cur, s, len);
461
+ out->cur += len;
462
+ *out->cur = '\0';
463
+ }
464
+ } else {
465
+ size = d2 * out->indent + 1;
466
+ for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
467
+ size_t nlen;
468
+
469
+ assure_size(out, size);
470
+ name = rb_id2name(*idp);
471
+ nlen = strlen(name);
472
+ if (0 != *fp) {
473
+ v = (*fp)(obj);
474
+ } else if (0 == strchr(name, '.')) {
475
+ v = rb_funcall(obj, *idp, 0);
476
+ } else {
477
+ char nbuf[256];
478
+ char *n2 = nbuf;
479
+ char *n;
480
+ char *end;
481
+ ID i;
482
+
483
+ if (sizeof(nbuf) <= nlen) {
484
+ if (NULL == (n2 = strdup(name))) {
485
+ rb_raise(rb_eNoMemError, "for attribute name.");
486
+ }
487
+ } else {
488
+ strcpy(n2, name);
489
+ }
490
+ n = n2;
491
+ v = obj;
492
+ while (0 != (end = strchr(n, '.'))) {
493
+ *end = '\0';
494
+ i = rb_intern(n);
495
+ v = rb_funcall(v, i, 0);
496
+ n = end + 1;
497
+ }
498
+ i = rb_intern(n);
499
+ v = rb_funcall(v, i, 0);
500
+ if (nbuf != n2) {
501
+ free(n2);
502
+ }
503
+ }
504
+ fill_indent(out, d2);
505
+ oj_dump_cstr(name, nlen, 0, 0, out);
506
+ *out->cur++ = ':';
507
+ oj_dump_custom_val(v, d2, out, true);
508
+ assure_size(out, 2);
509
+ *out->cur++ = ',';
510
+ }
511
+ out->cur--;
512
+ }
513
+ *out->cur++ = '}';
514
+ *out->cur = '\0';
515
+ }
516
+
517
+ // Return class if still needs dumping.
518
+ static VALUE
519
+ dump_common(VALUE obj, int depth, Out out) {
520
+
521
+ if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
522
+ oj_dump_raw_json(obj, depth, out);
523
+ } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
524
+ volatile VALUE rs;
525
+ const char *s;
526
+ int len;
527
+
528
+ if (Yes == out->opts->trace) {
529
+ oj_trace("to_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
530
+ }
531
+ if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
532
+ rs = rb_funcall(obj, oj_to_json_id, 0);
533
+ } else {
534
+ rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
535
+ }
536
+ if (Yes == out->opts->trace) {
537
+ oj_trace("to_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
538
+ }
539
+ s = rb_string_value_ptr((VALUE*)&rs);
540
+ len = (int)RSTRING_LEN(rs);
541
+
542
+ assure_size(out, len + 1);
543
+ memcpy(out->cur, s, len);
544
+ out->cur += len;
545
+ *out->cur = '\0';
546
+ } else if (Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
547
+ volatile VALUE aj;
548
+
549
+ if (Yes == out->opts->trace) {
550
+ oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
551
+ }
552
+ // Some classes elect to not take an options argument so check the arity
553
+ // of as_json.
554
+ if (0 == rb_obj_method_arity(obj, oj_as_json_id)) {
555
+ aj = rb_funcall(obj, oj_as_json_id, 0);
556
+ } else {
557
+ aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
558
+ }
559
+ if (Yes == out->opts->trace) {
560
+ oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
561
+ }
562
+ // Catch the obvious brain damaged recursive dumping.
563
+ if (aj == obj) {
564
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
565
+
566
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), false, false, out);
567
+ } else {
568
+ oj_dump_custom_val(aj, depth, out, true);
569
+ }
570
+ } else if (Yes == out->opts->to_hash && rb_respond_to(obj, oj_to_hash_id)) {
571
+ volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
572
+
573
+ if (T_HASH != rb_type(h)) {
574
+ // It seems that ActiveRecord implemented to_hash so that it returns
575
+ // an Array and not a Hash. To get around that any value returned
576
+ // will be dumped.
577
+
578
+ //rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
579
+ oj_dump_custom_val(h, depth, out, false);
580
+ } else {
581
+ dump_hash(h, depth, out, true);
582
+ }
583
+ } else if (!oj_code_dump(codes, obj, depth, out)) {
584
+ VALUE clas = rb_obj_class(obj);
585
+ Odd odd = oj_get_odd(clas);
586
+
587
+ if (NULL == odd) {
588
+ return clas;
589
+ }
590
+ dump_odd(obj, odd, clas, depth + 1, out);
591
+ }
592
+ return Qnil;
593
+ }
594
+
595
+ static int
596
+ dump_attr_cb(ID key, VALUE value, VALUE ov) {
597
+ Out out = (Out)ov;
598
+ int depth = out->depth;
599
+ size_t size;
600
+ const char *attr;
601
+
602
+ if (oj_dump_ignore(out->opts, value)) {
603
+ return ST_CONTINUE;
604
+ }
605
+ if (out->omit_nil && Qnil == value) {
606
+ return ST_CONTINUE;
607
+ }
608
+ size = depth * out->indent + 1;
609
+ attr = rb_id2name(key);
610
+ // Some exceptions such as NoMethodError have an invisible attribute where
611
+ // the key name is NULL. Not an empty string but NULL.
612
+ if (NULL == attr) {
613
+ attr = "";
614
+ } else if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
615
+ return ST_CONTINUE;
616
+ }
617
+ if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
618
+ return ST_CONTINUE;
619
+ }
620
+ assure_size(out, size);
621
+ fill_indent(out, depth);
622
+ if ('@' == *attr) {
623
+ attr++;
624
+ oj_dump_cstr(attr, strlen(attr), 0, 0, out);
625
+ } else {
626
+ char buf[32];
627
+
628
+ *buf = '~';
629
+ strncpy(buf + 1, attr, sizeof(buf) - 2);
630
+ buf[sizeof(buf) - 1] = '\0';
631
+ oj_dump_cstr(buf, strlen(buf), 0, 0, out);
632
+ }
633
+ *out->cur++ = ':';
634
+ oj_dump_custom_val(value, depth, out, true);
635
+ out->depth = depth;
636
+ *out->cur++ = ',';
637
+
638
+ return ST_CONTINUE;
639
+ }
640
+
641
+ static void
642
+ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
643
+ size_t size = 0;
644
+ int d2 = depth + 1;
645
+ int cnt;
646
+ bool class_written = false;
647
+
648
+ assure_size(out, 2);
649
+ *out->cur++ = '{';
650
+ if (Qundef != clas && NULL != out->opts->create_id && Yes == out->opts->create_ok) {
651
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
652
+ const char *classname = rb_obj_classname(obj);
653
+ size_t len = strlen(classname);
654
+
655
+ size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
656
+ assure_size(out, size);
657
+ fill_indent(out, d2);
658
+ *out->cur++ = '"';
659
+ memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
660
+ out->cur += out->opts->create_id_len;
661
+ *out->cur++ = '"';
662
+ if (0 < out->opts->dump_opts.before_size) {
663
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
664
+ out->cur += out->opts->dump_opts.before_size;
665
+ }
666
+ *out->cur++ = ':';
667
+ if (0 < out->opts->dump_opts.after_size) {
668
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
669
+ out->cur += out->opts->dump_opts.after_size;
670
+ }
671
+ *out->cur++ = '"';
672
+ memcpy(out->cur, classname, len);
673
+ out->cur += len;
674
+ *out->cur++ = '"';
675
+ class_written = true;
676
+ }
677
+ cnt = (int)rb_ivar_count(obj);
678
+ if (class_written) {
679
+ *out->cur++ = ',';
680
+ }
681
+ if (0 == cnt && Qundef == clas) {
682
+ // Might be something special like an Enumerable.
683
+ if (Qtrue == rb_obj_is_kind_of(obj, oj_enumerable_class)) {
684
+ out->cur--;
685
+ oj_dump_custom_val(rb_funcall(obj, rb_intern("entries"), 0), depth, out, false);
686
+ return;
687
+ }
688
+ }
689
+ out->depth = depth + 1;
690
+ rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
691
+ if (',' == *(out->cur - 1)) {
692
+ out->cur--; // backup to overwrite last comma
693
+ }
694
+ if (rb_obj_is_kind_of(obj, rb_eException)) {
695
+ volatile VALUE rv;
696
+
697
+ if (',' != *(out->cur - 1)) {
698
+ *out->cur++ = ',';
699
+ }
700
+ // message
701
+ assure_size(out, 2);
702
+ fill_indent(out, d2);
703
+ oj_dump_cstr("~mesg", 5, 0, 0, out);
704
+ *out->cur++ = ':';
705
+ rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
706
+ oj_dump_custom_val(rv, d2, out, true);
707
+ assure_size(out, size + 2);
708
+ *out->cur++ = ',';
709
+ // backtrace
710
+ fill_indent(out, d2);
711
+ oj_dump_cstr("~bt", 3, 0, 0, out);
712
+ *out->cur++ = ':';
713
+ rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
714
+ oj_dump_custom_val(rv, d2, out, true);
715
+ assure_size(out, 2);
716
+ }
717
+ out->depth = depth;
718
+
719
+ fill_indent(out, depth);
720
+ *out->cur++ = '}';
721
+ *out->cur = '\0';
722
+ }
723
+
724
+ static void
725
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
726
+ long id = oj_check_circular(obj, out);
727
+ VALUE clas;
728
+
729
+ if (0 > id) {
730
+ oj_dump_nil(Qnil, depth, out, false);
731
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
732
+ dump_obj_attrs(obj, clas, 0, depth, out);
733
+ }
734
+ *out->cur = '\0';
735
+ }
736
+
737
+ static void
738
+ dump_array(VALUE a, int depth, Out out, bool as_ok) {
739
+ size_t size;
740
+ int i, cnt;
741
+ int d2 = depth + 1;
742
+ long id = oj_check_circular(a, out);
743
+
744
+ if (0 > id) {
745
+ oj_dump_nil(Qnil, depth, out, false);
746
+ return;
747
+ }
748
+ cnt = (int)RARRAY_LEN(a);
749
+ *out->cur++ = '[';
750
+ assure_size(out, 2);
751
+ if (0 == cnt) {
752
+ *out->cur++ = ']';
753
+ } else {
754
+ if (out->opts->dump_opts.use) {
755
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
756
+ } else {
757
+ size = d2 * out->indent + 2;
758
+ }
759
+ cnt--;
760
+ for (i = 0; i <= cnt; i++) {
761
+ assure_size(out, size);
762
+ if (out->opts->dump_opts.use) {
763
+ if (0 < out->opts->dump_opts.array_size) {
764
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
765
+ out->cur += out->opts->dump_opts.array_size;
766
+ }
767
+ if (0 < out->opts->dump_opts.indent_size) {
768
+ int i;
769
+ for (i = d2; 0 < i; i--) {
770
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
771
+ out->cur += out->opts->dump_opts.indent_size;
772
+ }
773
+ }
774
+ } else {
775
+ fill_indent(out, d2);
776
+ }
777
+ oj_dump_custom_val(rb_ary_entry(a, i), d2, out, true);
778
+ if (i < cnt) {
779
+ *out->cur++ = ',';
780
+ }
781
+ }
782
+ size = depth * out->indent + 1;
783
+ assure_size(out, size);
784
+ if (out->opts->dump_opts.use) {
785
+ if (0 < out->opts->dump_opts.array_size) {
786
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
787
+ out->cur += out->opts->dump_opts.array_size;
788
+ }
789
+ if (0 < out->opts->dump_opts.indent_size) {
790
+ int i;
791
+
792
+ for (i = depth; 0 < i; i--) {
793
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
794
+ out->cur += out->opts->dump_opts.indent_size;
795
+ }
796
+ }
797
+ } else {
798
+ fill_indent(out, depth);
799
+ }
800
+ *out->cur++ = ']';
801
+ }
802
+ *out->cur = '\0';
803
+ }
804
+
805
+ static void
806
+ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
807
+ long id = oj_check_circular(obj, out);
808
+ VALUE clas;
809
+
810
+ if (0 > id) {
811
+ oj_dump_nil(Qnil, depth, out, false);
812
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
813
+ VALUE ma = Qnil;
814
+ VALUE v;
815
+ char num_id[32];
816
+ int i;
817
+ int d2 = depth + 1;
818
+ int d3 = d2 + 1;
819
+ size_t size = d2 * out->indent + d3 * out->indent + 3;
820
+ const char *name;
821
+ int cnt;
822
+ size_t len;
823
+
824
+ assure_size(out, size);
825
+ if (clas == rb_cRange) {
826
+ *out->cur++ = '"';
827
+ oj_dump_custom_val(rb_funcall(obj, oj_begin_id, 0), d3, out, false);
828
+ assure_size(out, 3);
829
+ *out->cur++ = '.';
830
+ *out->cur++ = '.';
831
+ if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
832
+ *out->cur++ = '.';
833
+ }
834
+ oj_dump_custom_val(rb_funcall(obj, oj_end_id, 0), d3, out, false);
835
+ *out->cur++ = '"';
836
+
837
+ return;
838
+ }
839
+ *out->cur++ = '{';
840
+ fill_indent(out, d2);
841
+ size = d3 * out->indent + 2;
842
+ ma = rb_struct_s_members(clas);
843
+
844
+ #ifdef RSTRUCT_LEN
845
+ #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
846
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
847
+ #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
848
+ cnt = (int)RSTRUCT_LEN(obj);
849
+ #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
850
+ #else
851
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
852
+ // class in interpreted Ruby so length() may not be defined.
853
+ cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
854
+ #endif
855
+ for (i = 0; i < cnt; i++) {
856
+ #ifdef RSTRUCT_LEN
857
+ v = RSTRUCT_GET(obj, i);
858
+ #else
859
+ v = rb_struct_aref(obj, INT2FIX(i));
860
+ #endif
861
+ if (ma != Qnil) {
862
+ volatile VALUE s = rb_sym_to_s(rb_ary_entry(ma, i));
863
+
864
+ name = rb_string_value_ptr((VALUE*)&s);
865
+ len = (int)RSTRING_LEN(s);
866
+ } else {
867
+ len = snprintf(num_id, sizeof(num_id), "%d", i);
868
+ name = num_id;
869
+ }
870
+ assure_size(out, size + len + 3);
871
+ fill_indent(out, d3);
872
+ *out->cur++ = '"';
873
+ memcpy(out->cur, name, len);
874
+ out->cur += len;
875
+ *out->cur++ = '"';
876
+ *out->cur++ = ':';
877
+ oj_dump_custom_val(v, d3, out, true);
878
+ *out->cur++ = ',';
879
+ }
880
+ out->cur--;
881
+ *out->cur++ = '}';
882
+ *out->cur = '\0';
883
+ }
884
+ }
885
+
886
+ static void
887
+ dump_data(VALUE obj, int depth, Out out, bool as_ok) {
888
+ long id = oj_check_circular(obj, out);
889
+ VALUE clas;
890
+
891
+ if (0 > id) {
892
+ oj_dump_nil(Qnil, depth, out, false);
893
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
894
+ dump_obj_attrs(obj, clas, id, depth, out);
895
+ }
896
+ }
897
+
898
+ static void
899
+ dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
900
+ if (NULL != out->opts->create_id) {
901
+ dump_obj_str(obj, depth, out);
902
+ } else {
903
+ dump_obj_as_str(obj, depth, out);
904
+ }
905
+ }
906
+
907
+ static void
908
+ dump_complex(VALUE obj, int depth, Out out, bool as_ok) {
909
+ complex_dump(obj, depth, out);
910
+ }
911
+
912
+ static void
913
+ dump_rational(VALUE obj, int depth, Out out, bool as_ok) {
914
+ rational_dump(obj, depth, out);
915
+ }
916
+
917
+ static DumpFunc custom_funcs[] = {
918
+ NULL, // RUBY_T_NONE = 0x00,
919
+ dump_obj, // RUBY_T_OBJECT = 0x01,
920
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
921
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
922
+ oj_dump_float, // RUBY_T_FLOAT = 0x04,
923
+ oj_dump_str, // RUBY_T_STRING = 0x05,
924
+ dump_regexp, // RUBY_T_REGEXP = 0x06,
925
+ dump_array, // RUBY_T_ARRAY = 0x07,
926
+ dump_hash, // RUBY_T_HASH = 0x08,
927
+ dump_struct, // RUBY_T_STRUCT = 0x09,
928
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
929
+ NULL, // RUBY_T_FILE = 0x0b,
930
+ dump_data, // RUBY_T_DATA = 0x0c,
931
+ NULL, // RUBY_T_MATCH = 0x0d,
932
+ dump_complex, // RUBY_T_COMPLEX = 0x0e,
933
+ dump_rational, // RUBY_T_RATIONAL = 0x0f,
934
+ NULL, // 0x10
935
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
936
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
937
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
938
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
939
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
940
+ };
941
+
942
+ void
943
+ oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
944
+ int type = rb_type(obj);
945
+
946
+ if (Yes == out->opts->trace) {
947
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
948
+ }
949
+ if (MAX_DEPTH < depth) {
950
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
951
+ }
952
+ if (0 < type && type <= RUBY_T_FIXNUM) {
953
+ DumpFunc f = custom_funcs[type];
954
+
955
+ if (NULL != f) {
956
+ f(obj, depth, out, true);
957
+ if (Yes == out->opts->trace) {
958
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
959
+ }
960
+ return;
961
+ }
962
+ }
963
+ oj_dump_nil(Qnil, depth, out, false);
964
+ if (Yes == out->opts->trace) {
965
+ oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
966
+ }
967
+ }
968
+
969
+ ///// load functions /////
970
+
971
+ static void
972
+ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
973
+ const char *key = kval->key;
974
+ int klen = kval->klen;
975
+ Val parent = stack_peek(&pi->stack);
976
+ volatile VALUE rkey = kval->key_val;
977
+
978
+ if (Qundef == rkey &&
979
+ Yes == pi->options.create_ok &&
980
+ NULL != pi->options.create_id &&
981
+ *pi->options.create_id == *key &&
982
+ (int)pi->options.create_id_len == klen &&
983
+ 0 == strncmp(pi->options.create_id, key, klen)) {
984
+
985
+ parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
986
+ if (2 == klen && '^' == *key && 'o' == key[1]) {
987
+ if (Qundef != parent->clas) {
988
+ if (!oj_code_has(codes, parent->clas, false)) {
989
+ parent->val = rb_obj_alloc(parent->clas);
990
+ }
991
+ }
992
+ }
993
+ } else {
994
+ volatile VALUE rstr = rb_str_new(str, len);
995
+
996
+ if (Qundef == rkey) {
997
+ rkey = rb_str_new(key, klen);
998
+ rstr = oj_encode(rstr);
999
+ rkey = oj_encode(rkey);
1000
+ if (Yes == pi->options.sym_key) {
1001
+ rkey = rb_str_intern(rkey);
1002
+ }
1003
+ }
1004
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
1005
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
1006
+
1007
+ if (Qnil != clas) {
1008
+ rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
1009
+ }
1010
+ }
1011
+ switch (rb_type(parent->val)) {
1012
+ case T_OBJECT:
1013
+ oj_set_obj_ivar(parent, kval, rstr);
1014
+ break;
1015
+ case T_HASH:
1016
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 == strncmp("time", parent->key, 4)) {
1017
+ if (Qnil == (parent->val = oj_parse_xml_time(str, (int)len))) {
1018
+ parent->val = rb_funcall(rb_cTime, rb_intern("parse"), 1, rb_str_new(str, len));
1019
+ }
1020
+ } else {
1021
+ rb_hash_aset(parent->val, rkey, rstr);
1022
+ }
1023
+ break;
1024
+ default:
1025
+ break;
1026
+ }
1027
+ if (Yes == pi->options.trace) {
1028
+ oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rstr);
1029
+ }
1030
+ }
1031
+ }
1032
+
1033
+ static void
1034
+ end_hash(struct _parseInfo *pi) {
1035
+ Val parent = stack_peek(&pi->stack);
1036
+
1037
+ if (Qundef != parent->clas && parent->clas != rb_obj_class(parent->val)) {
1038
+ volatile VALUE obj = oj_code_load(codes, parent->clas, parent->val);
1039
+
1040
+ if (Qnil != obj) {
1041
+ parent->val = obj;
1042
+ } else {
1043
+ parent->val = rb_funcall(parent->clas, oj_json_create_id, 1, parent->val);
1044
+ }
1045
+ parent->clas = Qundef;
1046
+ }
1047
+ if (Yes == pi->options.trace) {
1048
+ oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
1049
+ }
1050
+ }
1051
+
1052
+ static VALUE
1053
+ calc_hash_key(ParseInfo pi, Val parent) {
1054
+ volatile VALUE rkey = parent->key_val;
1055
+
1056
+ if (Qundef == rkey) {
1057
+ rkey = rb_str_new(parent->key, parent->klen);
1058
+ }
1059
+ rkey = oj_encode(rkey);
1060
+ if (Yes == pi->options.sym_key) {
1061
+ rkey = rb_str_intern(rkey);
1062
+ }
1063
+ return rkey;
1064
+ }
1065
+
1066
+ static void
1067
+ hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
1068
+ Val parent = stack_peek(&pi->stack);
1069
+ volatile VALUE rval = oj_num_as_value(ni);
1070
+
1071
+ switch (rb_type(parent->val)) {
1072
+ case T_OBJECT:
1073
+ oj_set_obj_ivar(parent, kval, rval);
1074
+ break;
1075
+ case T_HASH:
1076
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 != ni->div && 0 == strncmp("time", parent->key, 4)) {
1077
+ int64_t nsec = ni->num * 1000000000LL / ni->div;
1078
+
1079
+ if (ni->neg) {
1080
+ ni->i = -ni->i;
1081
+ if (0 < nsec) {
1082
+ ni->i--;
1083
+ nsec = 1000000000LL - nsec;
1084
+ }
1085
+ }
1086
+ if (86400 == ni->exp) { // UTC time
1087
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
1088
+ // Since the ruby C routines alway create local time, the
1089
+ // offset and then a conversion to UTC keeps makes the time
1090
+ // match the expected value.
1091
+ parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
1092
+ } else if (ni->hasExp) {
1093
+ int64_t t = (int64_t)(ni->i + ni->exp);
1094
+ struct _timeInfo ti;
1095
+ VALUE args[8];
1096
+
1097
+ sec_as_time(t, &ti);
1098
+
1099
+ args[0] = LONG2NUM(ti.year);
1100
+ args[1] = LONG2NUM(ti.mon);
1101
+ args[2] = LONG2NUM(ti.day);
1102
+ args[3] = LONG2NUM(ti.hour);
1103
+ args[4] = LONG2NUM(ti.min);
1104
+ args[5] = rb_float_new((double)ti.sec + ((double)nsec + 0.5) / 1000000000.0);
1105
+ args[6] = LONG2NUM(ni->exp);
1106
+ parent->val = rb_funcall2(rb_cTime, oj_new_id, 7, args);
1107
+ } else {
1108
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
1109
+ }
1110
+ rval = parent->val;
1111
+ } else {
1112
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval), rval);
1113
+ }
1114
+ break;
1115
+ default:
1116
+ break;
1117
+ }
1118
+ if (Yes == pi->options.trace) {
1119
+ oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
1120
+ }
1121
+ }
1122
+
1123
+ static void
1124
+ hash_set_value(ParseInfo pi, Val kval, VALUE value) {
1125
+ Val parent = stack_peek(&pi->stack);
1126
+
1127
+ switch (rb_type(parent->val)) {
1128
+ case T_OBJECT:
1129
+ oj_set_obj_ivar(parent, kval, value);
1130
+ break;
1131
+ case T_HASH:
1132
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval), value);
1133
+ break;
1134
+ default:
1135
+ break;
1136
+ }
1137
+ if (Yes == pi->options.trace) {
1138
+ oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
1139
+ }
1140
+ }
1141
+
1142
+ static void
1143
+ array_append_num(ParseInfo pi, NumInfo ni) {
1144
+ Val parent = stack_peek(&pi->stack);
1145
+ volatile VALUE rval = oj_num_as_value(ni);
1146
+
1147
+ rb_ary_push(parent->val, rval);
1148
+ if (Yes == pi->options.trace) {
1149
+ oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
1150
+ }
1151
+ }
1152
+
1153
+ static void
1154
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
1155
+ volatile VALUE rstr = rb_str_new(str, len);
1156
+
1157
+ rstr = oj_encode(rstr);
1158
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
1159
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
1160
+
1161
+ if (Qnil != clas) {
1162
+ rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
1163
+ return;
1164
+ }
1165
+ }
1166
+ rb_ary_push(stack_peek(&pi->stack)->val, rstr);
1167
+ if (Yes == pi->options.trace) {
1168
+ oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rstr);
1169
+ }
1170
+ }
1171
+
1172
+ void
1173
+ oj_set_custom_callbacks(ParseInfo pi) {
1174
+ oj_set_compat_callbacks(pi);
1175
+ pi->hash_set_cstr = hash_set_cstr;
1176
+ pi->end_hash = end_hash;
1177
+ pi->hash_set_num = hash_set_num;
1178
+ pi->hash_set_value = hash_set_value;
1179
+ pi->array_append_cstr = array_append_cstr;
1180
+ pi->array_append_num = array_append_num;
1181
+ }
1182
+
1183
+ VALUE
1184
+ oj_custom_parse(int argc, VALUE *argv, VALUE self) {
1185
+ struct _parseInfo pi;
1186
+
1187
+ parse_info_init(&pi);
1188
+ pi.options = oj_default_options;
1189
+ pi.handler = Qnil;
1190
+ pi.err_class = Qnil;
1191
+ pi.max_depth = 0;
1192
+ pi.options.allow_nan = Yes;
1193
+ pi.options.nilnil = Yes;
1194
+ oj_set_custom_callbacks(&pi);
1195
+
1196
+ if (T_STRING == rb_type(*argv)) {
1197
+ return oj_pi_parse(argc, argv, &pi, 0, 0, false);
1198
+ } else {
1199
+ return oj_pi_sparse(argc, argv, &pi, 0);
1200
+ }
1201
+ }
1202
+
1203
+ VALUE
1204
+ oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
1205
+ struct _parseInfo pi;
1206
+
1207
+ parse_info_init(&pi);
1208
+ pi.options = oj_default_options;
1209
+ pi.handler = Qnil;
1210
+ pi.err_class = Qnil;
1211
+ pi.max_depth = 0;
1212
+ pi.options.allow_nan = Yes;
1213
+ pi.options.nilnil = Yes;
1214
+ oj_set_custom_callbacks(&pi);
1215
+ pi.end_hash = end_hash;
1216
+
1217
+ return oj_pi_parse(argc, argv, &pi, json, len, false);
1218
+ }