oj 3.8.1

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