oj 2.18.5 → 3.16.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1452 -0
  3. data/README.md +53 -221
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +54 -72
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +61 -63
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +38 -67
  11. data/ext/oj/circarray.h +16 -42
  12. data/ext/oj/code.c +214 -0
  13. data/ext/oj/code.h +40 -0
  14. data/ext/oj/compat.c +194 -110
  15. data/ext/oj/custom.c +1074 -0
  16. data/ext/oj/debug.c +126 -0
  17. data/ext/oj/dump.c +1276 -2494
  18. data/ext/oj/dump.h +110 -0
  19. data/ext/oj/dump_compat.c +897 -0
  20. data/ext/oj/dump_leaf.c +162 -0
  21. data/ext/oj/dump_object.c +710 -0
  22. data/ext/oj/dump_strict.c +399 -0
  23. data/ext/oj/encode.h +7 -42
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +28 -53
  26. data/ext/oj/err.h +49 -46
  27. data/ext/oj/extconf.rb +33 -32
  28. data/ext/oj/fast.c +1082 -1098
  29. data/ext/oj/intern.c +313 -0
  30. data/ext/oj/intern.h +22 -0
  31. data/ext/oj/mem.c +318 -0
  32. data/ext/oj/mem.h +53 -0
  33. data/ext/oj/mimic_json.c +919 -0
  34. data/ext/oj/object.c +545 -625
  35. data/ext/oj/odd.c +158 -168
  36. data/ext/oj/odd.h +32 -58
  37. data/ext/oj/oj.c +1727 -2080
  38. data/ext/oj/oj.h +334 -259
  39. data/ext/oj/parse.c +974 -753
  40. data/ext/oj/parse.h +97 -90
  41. data/ext/oj/parser.c +1600 -0
  42. data/ext/oj/parser.h +103 -0
  43. data/ext/oj/rails.c +1478 -0
  44. data/ext/oj/rails.h +18 -0
  45. data/ext/oj/reader.c +136 -163
  46. data/ext/oj/reader.h +76 -112
  47. data/ext/oj/resolve.c +45 -94
  48. data/ext/oj/resolve.h +7 -34
  49. data/ext/oj/rxclass.c +144 -0
  50. data/ext/oj/rxclass.h +26 -0
  51. data/ext/oj/saj.c +445 -511
  52. data/ext/oj/saj2.c +584 -0
  53. data/ext/oj/saj2.h +23 -0
  54. data/ext/oj/scp.c +82 -143
  55. data/ext/oj/simd.h +10 -0
  56. data/ext/oj/sparse.c +749 -644
  57. data/ext/oj/stream_writer.c +329 -0
  58. data/ext/oj/strict.c +114 -112
  59. data/ext/oj/string_writer.c +517 -0
  60. data/ext/oj/trace.c +72 -0
  61. data/ext/oj/trace.h +55 -0
  62. data/ext/oj/usual.c +1218 -0
  63. data/ext/oj/usual.h +69 -0
  64. data/ext/oj/util.c +136 -0
  65. data/ext/oj/util.h +20 -0
  66. data/ext/oj/val_stack.c +75 -72
  67. data/ext/oj/val_stack.h +94 -127
  68. data/ext/oj/validate.c +46 -0
  69. data/ext/oj/wab.c +586 -0
  70. data/lib/oj/active_support_helper.rb +1 -3
  71. data/lib/oj/bag.rb +8 -1
  72. data/lib/oj/easy_hash.rb +21 -13
  73. data/lib/oj/error.rb +10 -12
  74. data/lib/oj/json.rb +188 -0
  75. data/lib/oj/mimic.rb +165 -26
  76. data/lib/oj/saj.rb +20 -6
  77. data/lib/oj/schandler.rb +5 -4
  78. data/lib/oj/state.rb +135 -0
  79. data/lib/oj/version.rb +2 -3
  80. data/lib/oj.rb +3 -31
  81. data/pages/Advanced.md +22 -0
  82. data/pages/Compatibility.md +25 -0
  83. data/pages/Custom.md +23 -0
  84. data/pages/Encoding.md +65 -0
  85. data/pages/InstallOptions.md +20 -0
  86. data/pages/JsonGem.md +94 -0
  87. data/pages/Modes.md +161 -0
  88. data/pages/Options.md +337 -0
  89. data/pages/Parser.md +309 -0
  90. data/pages/Rails.md +167 -0
  91. data/pages/Security.md +20 -0
  92. data/pages/WAB.md +13 -0
  93. metadata +126 -163
  94. data/ext/oj/hash.c +0 -163
  95. data/ext/oj/hash.h +0 -46
  96. data/ext/oj/hash_test.c +0 -512
  97. data/test/_test_active.rb +0 -76
  98. data/test/_test_active_mimic.rb +0 -96
  99. data/test/_test_mimic_rails.rb +0 -126
  100. data/test/activesupport_datetime_test.rb +0 -23
  101. data/test/bug.rb +0 -51
  102. data/test/bug2.rb +0 -10
  103. data/test/bug3.rb +0 -46
  104. data/test/bug_fast.rb +0 -32
  105. data/test/bug_load.rb +0 -24
  106. data/test/crash.rb +0 -111
  107. data/test/curl/curl_oj.rb +0 -46
  108. data/test/curl/get_oj.rb +0 -24
  109. data/test/curl/just_curl.rb +0 -31
  110. data/test/curl/just_oj.rb +0 -51
  111. data/test/example.rb +0 -11
  112. data/test/files.rb +0 -29
  113. data/test/foo.rb +0 -24
  114. data/test/helper.rb +0 -27
  115. data/test/io.rb +0 -48
  116. data/test/isolated/shared.rb +0 -310
  117. data/test/isolated/test_mimic_after.rb +0 -13
  118. data/test/isolated/test_mimic_alone.rb +0 -12
  119. data/test/isolated/test_mimic_as_json.rb +0 -45
  120. data/test/isolated/test_mimic_before.rb +0 -13
  121. data/test/isolated/test_mimic_define.rb +0 -28
  122. data/test/isolated/test_mimic_rails_after.rb +0 -22
  123. data/test/isolated/test_mimic_rails_before.rb +0 -21
  124. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  125. data/test/isolated/test_mimic_redefine.rb +0 -15
  126. data/test/mod.rb +0 -16
  127. data/test/perf.rb +0 -107
  128. data/test/perf_compat.rb +0 -128
  129. data/test/perf_fast.rb +0 -164
  130. data/test/perf_file.rb +0 -64
  131. data/test/perf_object.rb +0 -138
  132. data/test/perf_saj.rb +0 -109
  133. data/test/perf_scp.rb +0 -151
  134. data/test/perf_simple.rb +0 -287
  135. data/test/perf_strict.rb +0 -128
  136. data/test/rails.rb +0 -50
  137. data/test/russian.rb +0 -18
  138. data/test/sample/change.rb +0 -14
  139. data/test/sample/dir.rb +0 -19
  140. data/test/sample/doc.rb +0 -36
  141. data/test/sample/file.rb +0 -48
  142. data/test/sample/group.rb +0 -16
  143. data/test/sample/hasprops.rb +0 -16
  144. data/test/sample/layer.rb +0 -12
  145. data/test/sample/line.rb +0 -20
  146. data/test/sample/oval.rb +0 -10
  147. data/test/sample/rect.rb +0 -10
  148. data/test/sample/shape.rb +0 -35
  149. data/test/sample/text.rb +0 -20
  150. data/test/sample.rb +0 -55
  151. data/test/sample_json.rb +0 -37
  152. data/test/struct.rb +0 -29
  153. data/test/test_compat.rb +0 -398
  154. data/test/test_debian.rb +0 -53
  155. data/test/test_fast.rb +0 -458
  156. data/test/test_file.rb +0 -245
  157. data/test/test_gc.rb +0 -49
  158. data/test/test_hash.rb +0 -29
  159. data/test/test_object.rb +0 -745
  160. data/test/test_saj.rb +0 -186
  161. data/test/test_scp.rb +0 -396
  162. data/test/test_serializer.rb +0 -59
  163. data/test/test_strict.rb +0 -254
  164. data/test/test_various.rb +0 -1383
  165. data/test/test_writer.rb +0 -308
  166. data/test/write_timebars.rb +0 -31
data/ext/oj/custom.c ADDED
@@ -0,0 +1,1074 @@
1
+ // Copyright (c) 2012, 2017 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #include <stdint.h>
5
+ #include <stdio.h>
6
+
7
+ #include "code.h"
8
+ #include "dump.h"
9
+ #include "encode.h"
10
+ #include "err.h"
11
+ #include "intern.h"
12
+ #include "mem.h"
13
+ #include "odd.h"
14
+ #include "oj.h"
15
+ #include "parse.h"
16
+ #include "resolve.h"
17
+ #include "trace.h"
18
+ #include "util.h"
19
+
20
+ extern void oj_set_obj_ivar(Val parent, Val kval, VALUE value);
21
+ extern VALUE oj_parse_xml_time(const char *str, int len); // from object.c
22
+
23
+ static void dump_obj_str(VALUE obj, int depth, Out out) {
24
+ struct _attr attrs[] = {
25
+ {"s", 1, Qnil},
26
+ {NULL, 0, Qnil},
27
+ };
28
+ attrs->value = oj_safe_string_convert(obj);
29
+
30
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
31
+ }
32
+
33
+ static void dump_obj_as_str(VALUE obj, int depth, Out out) {
34
+ volatile VALUE rstr = oj_safe_string_convert(obj);
35
+ const char *str = RSTRING_PTR(rstr);
36
+
37
+ oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
38
+ }
39
+
40
+ static void bigdecimal_dump(VALUE obj, int depth, Out out) {
41
+ volatile VALUE rstr = oj_safe_string_convert(obj);
42
+ const char *str = RSTRING_PTR(rstr);
43
+ size_t len = RSTRING_LEN(rstr);
44
+
45
+ if (0 == strcasecmp("Infinity", str)) {
46
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
47
+ oj_dump_raw(str, len, out);
48
+ } else if (0 == strcasecmp("-Infinity", str)) {
49
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
50
+ oj_dump_raw(str, len, out);
51
+ } else if (No == out->opts->bigdec_as_num) {
52
+ oj_dump_cstr(str, len, 0, 0, out);
53
+ } else {
54
+ oj_dump_raw(str, len, out);
55
+ }
56
+ }
57
+
58
+ static ID real_id = 0;
59
+ static ID imag_id = 0;
60
+
61
+ static void complex_dump(VALUE obj, int depth, Out out) {
62
+ if (NULL != out->opts->create_id) {
63
+ struct _attr attrs[] = {
64
+ {"real", 4, Qnil},
65
+ {"imag", 4, Qnil},
66
+ {NULL, 0, Qnil},
67
+ };
68
+ if (0 == real_id) {
69
+ real_id = rb_intern("real");
70
+ imag_id = rb_intern("imag");
71
+ }
72
+ attrs[0].value = rb_funcall(obj, real_id, 0);
73
+ attrs[1].value = rb_funcall(obj, imag_id, 0);
74
+
75
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
76
+ } else {
77
+ dump_obj_as_str(obj, depth, out);
78
+ }
79
+ }
80
+
81
+ static VALUE complex_load(VALUE clas, VALUE args) {
82
+ if (0 == real_id) {
83
+ real_id = rb_intern("real");
84
+ imag_id = rb_intern("imag");
85
+ }
86
+ return rb_complex_new(rb_hash_aref(args, rb_id2str(real_id)), rb_hash_aref(args, rb_id2str(imag_id)));
87
+ }
88
+
89
+ static void time_dump(VALUE obj, int depth, Out out) {
90
+ if (Yes == out->opts->create_ok) {
91
+ struct _attr attrs[] = {
92
+ {"time", 4, Qundef, 0, Qundef},
93
+ {NULL, 0, Qnil},
94
+ };
95
+ attrs->time = obj;
96
+
97
+ oj_code_attrs(obj, attrs, depth, out, true);
98
+ } else {
99
+ switch (out->opts->time_format) {
100
+ case RubyTime: oj_dump_ruby_time(obj, out); break;
101
+ case XmlTime: oj_dump_xml_time(obj, out); break;
102
+ case UnixZTime: oj_dump_time(obj, out, true); break;
103
+ case UnixTime:
104
+ default: oj_dump_time(obj, out, false); break;
105
+ }
106
+ }
107
+ }
108
+
109
+ static void date_dump(VALUE obj, int depth, Out out) {
110
+ if (Yes == out->opts->create_ok) {
111
+ struct _attr attrs[] = {
112
+ {"s", 1, Qnil},
113
+ {NULL, 0, Qnil},
114
+ };
115
+ attrs->value = rb_funcall(obj, rb_intern("iso8601"), 0);
116
+
117
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
118
+ } else {
119
+ volatile VALUE v;
120
+ volatile VALUE ov;
121
+
122
+ switch (out->opts->time_format) {
123
+ case RubyTime:
124
+ case XmlTime:
125
+ v = rb_funcall(obj, rb_intern("iso8601"), 0);
126
+ oj_dump_cstr(RSTRING_PTR(v), RSTRING_LEN(v), 0, 0, out);
127
+ break;
128
+ case UnixZTime:
129
+ v = rb_funcall(obj, rb_intern("to_time"), 0);
130
+ if (oj_date_class == rb_obj_class(obj)) {
131
+ ov = rb_funcall(v, rb_intern("utc_offset"), 0);
132
+ v = rb_funcall(v, rb_intern("utc"), 0);
133
+ v = rb_funcall(v, rb_intern("+"), 1, ov);
134
+ oj_dump_time(v, out, false);
135
+ } else {
136
+ oj_dump_time(v, out, true);
137
+ }
138
+ break;
139
+ case UnixTime:
140
+ default:
141
+ v = rb_funcall(obj, rb_intern("to_time"), 0);
142
+ if (oj_date_class == rb_obj_class(obj)) {
143
+ ov = rb_funcall(v, rb_intern("utc_offset"), 0);
144
+ v = rb_funcall(v, rb_intern("utc"), 0);
145
+ v = rb_funcall(v, rb_intern("+"), 1, ov);
146
+ }
147
+ oj_dump_time(v, out, false);
148
+ break;
149
+ }
150
+ }
151
+ }
152
+
153
+ static VALUE date_load(VALUE clas, VALUE args) {
154
+ volatile VALUE v;
155
+
156
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
157
+ return rb_funcall(oj_date_class, rb_intern("parse"), 1, v);
158
+ }
159
+ return Qnil;
160
+ }
161
+
162
+ static VALUE datetime_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_datetime_class, rb_intern("parse"), 1, v);
167
+ }
168
+ return Qnil;
169
+ }
170
+
171
+ static ID table_id = 0;
172
+
173
+ static void 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 openstruct_load(VALUE clas, VALUE args) {
187
+ if (0 == table_id) {
188
+ table_id = rb_intern("table");
189
+ }
190
+ return rb_funcall(clas, oj_new_id, 1, rb_hash_aref(args, rb_id2str(table_id)));
191
+ }
192
+
193
+ static void range_dump(VALUE obj, int depth, Out out) {
194
+ if (NULL != out->opts->create_id) {
195
+ struct _attr attrs[] = {
196
+ {"begin", 5, Qnil},
197
+ {"end", 3, Qnil},
198
+ {"exclude", 7, Qnil},
199
+ {NULL, 0, Qnil},
200
+ };
201
+ attrs[0].value = rb_funcall(obj, oj_begin_id, 0);
202
+ attrs[1].value = rb_funcall(obj, oj_end_id, 0);
203
+ attrs[2].value = rb_funcall(obj, oj_exclude_end_id, 0);
204
+
205
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
206
+ } else {
207
+ dump_obj_as_str(obj, depth, out);
208
+ }
209
+ }
210
+
211
+ static VALUE range_load(VALUE clas, VALUE args) {
212
+ VALUE nargs[3];
213
+
214
+ nargs[0] = rb_hash_aref(args, rb_id2str(oj_begin_id));
215
+ nargs[1] = rb_hash_aref(args, rb_id2str(oj_end_id));
216
+ nargs[2] = rb_hash_aref(args, rb_id2str(oj_exclude_end_id));
217
+
218
+ return rb_class_new_instance(3, nargs, rb_cRange);
219
+ }
220
+
221
+ static ID numerator_id = 0;
222
+ static ID denominator_id = 0;
223
+
224
+ static void rational_dump(VALUE obj, int depth, Out out) {
225
+ if (NULL != out->opts->create_id) {
226
+ struct _attr attrs[] = {
227
+ {"numerator", 9, Qnil},
228
+ {"denominator", 11, Qnil},
229
+ {NULL, 0, Qnil},
230
+ };
231
+ if (0 == numerator_id) {
232
+ numerator_id = rb_intern("numerator");
233
+ denominator_id = rb_intern("denominator");
234
+ }
235
+ attrs[0].value = rb_funcall(obj, numerator_id, 0);
236
+ attrs[1].value = rb_funcall(obj, denominator_id, 0);
237
+
238
+ oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
239
+ } else {
240
+ dump_obj_as_str(obj, depth, out);
241
+ }
242
+ }
243
+
244
+ static VALUE rational_load(VALUE clas, VALUE args) {
245
+ if (0 == numerator_id) {
246
+ numerator_id = rb_intern("numerator");
247
+ denominator_id = rb_intern("denominator");
248
+ }
249
+ return rb_rational_new(rb_hash_aref(args, rb_id2str(numerator_id)), rb_hash_aref(args, rb_id2str(denominator_id)));
250
+ }
251
+
252
+ static VALUE regexp_load(VALUE clas, VALUE args) {
253
+ volatile VALUE v;
254
+
255
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
256
+ return rb_funcall(rb_cRegexp, oj_new_id, 1, v);
257
+ }
258
+ return Qnil;
259
+ }
260
+
261
+ static VALUE 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 hash_cb(VALUE key, VALUE value, VALUE ov) {
281
+ Out out = (Out)ov;
282
+ int depth = out->depth;
283
+
284
+ if (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
+ APPEND_CHARS(out->cur, out->opts->dump_opts.hash_nl, out->opts->dump_opts.hash_size);
297
+ }
298
+ if (0 < out->opts->dump_opts.indent_size) {
299
+ int i;
300
+
301
+ for (i = depth; 0 < i; i--) {
302
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
303
+ }
304
+ }
305
+ }
306
+ switch (rb_type(key)) {
307
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
308
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
309
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
310
+ }
311
+ if (!out->opts->dump_opts.use) {
312
+ *out->cur++ = ':';
313
+ } else {
314
+ assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
315
+ if (0 < out->opts->dump_opts.before_size) {
316
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
317
+ }
318
+ *out->cur++ = ':';
319
+ if (0 < out->opts->dump_opts.after_size) {
320
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
321
+ }
322
+ }
323
+ oj_dump_custom_val(value, depth, out, true);
324
+ out->depth = depth;
325
+ *out->cur++ = ',';
326
+
327
+ return ST_CONTINUE;
328
+ }
329
+
330
+ static void dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
331
+ int cnt;
332
+ long id = oj_check_circular(obj, out);
333
+
334
+ if (0 > id) {
335
+ oj_dump_nil(Qnil, depth, out, false);
336
+ return;
337
+ }
338
+ cnt = (int)RHASH_SIZE(obj);
339
+ assure_size(out, 2);
340
+ if (0 == cnt) {
341
+ APPEND_CHARS(out->cur, "{}", 2);
342
+ } else {
343
+ *out->cur++ = '{';
344
+ out->depth = depth + 1;
345
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
346
+ if (',' == *(out->cur - 1)) {
347
+ out->cur--; // backup to overwrite last comma
348
+ }
349
+ if (!out->opts->dump_opts.use) {
350
+ assure_size(out, depth * out->indent + 2);
351
+ fill_indent(out, depth);
352
+ } else {
353
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
354
+ if (0 < out->opts->dump_opts.hash_size) {
355
+ APPEND_CHARS(out->cur, out->opts->dump_opts.hash_nl, out->opts->dump_opts.hash_size);
356
+ }
357
+ if (0 < out->opts->dump_opts.indent_size) {
358
+ int i;
359
+
360
+ for (i = depth; 0 < i; i--) {
361
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
362
+ }
363
+ }
364
+ }
365
+ *out->cur++ = '}';
366
+ }
367
+ *out->cur = '\0';
368
+ }
369
+
370
+ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
371
+ ID *idp;
372
+ AttrGetFunc *fp;
373
+ volatile VALUE v;
374
+ const char *name;
375
+ size_t size;
376
+ int d2 = depth + 1;
377
+
378
+ assure_size(out, 2);
379
+ *out->cur++ = '{';
380
+ if (NULL != out->opts->create_id && Yes == out->opts->create_ok) {
381
+ const char *classname = rb_class2name(clas);
382
+ int clen = (int)strlen(classname);
383
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
384
+
385
+ size = d2 * out->indent + 10 + clen + out->opts->create_id_len + sep_len;
386
+ assure_size(out, size);
387
+ fill_indent(out, d2);
388
+ *out->cur++ = '"';
389
+ APPEND_CHARS(out->cur, out->opts->create_id, out->opts->create_id_len);
390
+ *out->cur++ = '"';
391
+ if (0 < out->opts->dump_opts.before_size) {
392
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
393
+ }
394
+ *out->cur++ = ':';
395
+ if (0 < out->opts->dump_opts.after_size) {
396
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
397
+ }
398
+ *out->cur++ = '"';
399
+ APPEND_CHARS(out->cur, classname, clen);
400
+ APPEND_CHARS(out->cur, "\",", 2);
401
+ }
402
+ if (odd->raw) {
403
+ v = rb_funcall(obj, *odd->attrs, 0);
404
+ if (Qundef == v || T_STRING != rb_type(v)) {
405
+ rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
406
+ } else {
407
+ const char *s = RSTRING_PTR(v);
408
+ size_t len = RSTRING_LEN(v);
409
+ const char *name = rb_id2name(*odd->attrs);
410
+ size_t nlen = strlen(name);
411
+
412
+ size = len + d2 * out->indent + nlen + 10;
413
+ assure_size(out, size);
414
+ fill_indent(out, d2);
415
+ *out->cur++ = '"';
416
+ APPEND_CHARS(out->cur, name, nlen);
417
+ APPEND_CHARS(out->cur, "\":", 2);
418
+ APPEND_CHARS(out->cur, s, len);
419
+ *out->cur = '\0';
420
+ }
421
+ } else {
422
+ size = d2 * out->indent + 1;
423
+ for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
424
+ size_t nlen;
425
+
426
+ assure_size(out, size);
427
+ name = rb_id2name(*idp);
428
+ nlen = strlen(name);
429
+ if (0 != *fp) {
430
+ v = (*fp)(obj);
431
+ } else if (0 == strchr(name, '.')) {
432
+ v = rb_funcall(obj, *idp, 0);
433
+ } else {
434
+ char nbuf[256];
435
+ char *n2 = nbuf;
436
+ char *n;
437
+ char *end;
438
+ ID i;
439
+
440
+ if (sizeof(nbuf) <= nlen) {
441
+ if (NULL == (n2 = OJ_STRDUP(name))) {
442
+ rb_raise(rb_eNoMemError, "for attribute name.");
443
+ }
444
+ } else {
445
+ strcpy(n2, name);
446
+ }
447
+ n = n2;
448
+ v = obj;
449
+ while (0 != (end = strchr(n, '.'))) {
450
+ *end = '\0';
451
+ i = rb_intern(n);
452
+ v = rb_funcall(v, i, 0);
453
+ n = end + 1;
454
+ }
455
+ i = rb_intern(n);
456
+ v = rb_funcall(v, i, 0);
457
+ if (nbuf != n2) {
458
+ OJ_FREE(n2);
459
+ }
460
+ }
461
+ fill_indent(out, d2);
462
+ oj_dump_cstr(name, nlen, 0, 0, out);
463
+ *out->cur++ = ':';
464
+ oj_dump_custom_val(v, d2, out, true);
465
+ assure_size(out, 2);
466
+ *out->cur++ = ',';
467
+ }
468
+ out->cur--;
469
+ }
470
+ *out->cur++ = '}';
471
+ *out->cur = '\0';
472
+ }
473
+
474
+ // Return class if still needs dumping.
475
+ static VALUE dump_common(VALUE obj, int depth, Out out) {
476
+ if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
477
+ oj_dump_raw_json(obj, depth, out);
478
+ } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
479
+ volatile VALUE rs;
480
+ const char *s;
481
+ size_t len;
482
+
483
+ TRACE(out->opts->trace, "to_json", obj, depth + 1, TraceRubyIn);
484
+ if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
485
+ rs = rb_funcall(obj, oj_to_json_id, 0);
486
+ } else {
487
+ rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
488
+ }
489
+ TRACE(out->opts->trace, "to_json", obj, depth + 1, TraceRubyOut);
490
+ s = RSTRING_PTR(rs);
491
+ len = RSTRING_LEN(rs);
492
+
493
+ assure_size(out, len + 1);
494
+ APPEND_CHARS(out->cur, s, len);
495
+ *out->cur = '\0';
496
+ } else if (Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
497
+ volatile VALUE aj;
498
+
499
+ TRACE(out->opts->trace, "as_json", obj, depth + 1, TraceRubyIn);
500
+ // Some classes elect to not take an options argument so check the arity
501
+ // of as_json.
502
+ if (0 == rb_obj_method_arity(obj, oj_as_json_id)) {
503
+ aj = rb_funcall(obj, oj_as_json_id, 0);
504
+ } else {
505
+ aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
506
+ }
507
+ TRACE(out->opts->trace, "as_json", obj, depth + 1, TraceRubyOut);
508
+ // Catch the obvious brain damaged recursive dumping.
509
+ if (aj == obj) {
510
+ volatile VALUE rstr = oj_safe_string_convert(obj);
511
+
512
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), false, false, out);
513
+ } else {
514
+ oj_dump_custom_val(aj, depth, out, true);
515
+ }
516
+ } else if (Yes == out->opts->to_hash && rb_respond_to(obj, oj_to_hash_id)) {
517
+ volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
518
+
519
+ if (T_HASH != rb_type(h)) {
520
+ // It seems that ActiveRecord implemented to_hash so that it returns
521
+ // an Array and not a Hash. To get around that any value returned
522
+ // will be dumped.
523
+
524
+ // rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n",
525
+ // rb_class2name(rb_obj_class(obj)));
526
+ oj_dump_custom_val(h, depth, out, false);
527
+ } else {
528
+ dump_hash(h, depth, out, true);
529
+ }
530
+ } else if (!oj_code_dump(codes, obj, depth, out)) {
531
+ VALUE clas = rb_obj_class(obj);
532
+ Odd odd = oj_get_odd(clas);
533
+
534
+ if (NULL == odd) {
535
+ return clas;
536
+ }
537
+ dump_odd(obj, odd, clas, depth + 1, out);
538
+ }
539
+ return Qnil;
540
+ }
541
+
542
+ static int dump_attr_cb(ID key, VALUE value, VALUE ov) {
543
+ Out out = (Out)ov;
544
+ int depth = out->depth;
545
+ size_t size;
546
+ const char *attr;
547
+
548
+ if (dump_ignore(out->opts, value)) {
549
+ return ST_CONTINUE;
550
+ }
551
+ if (out->omit_nil && Qnil == value) {
552
+ return ST_CONTINUE;
553
+ }
554
+ size = depth * out->indent + 1;
555
+ attr = rb_id2name(key);
556
+ // Some exceptions such as NoMethodError have an invisible attribute where
557
+ // the key name is NULL. Not an empty string but NULL.
558
+ if (NULL == attr) {
559
+ attr = "";
560
+ } else if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
561
+ return ST_CONTINUE;
562
+ }
563
+ if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
564
+ return ST_CONTINUE;
565
+ }
566
+ assure_size(out, size);
567
+ fill_indent(out, depth);
568
+ if ('@' == *attr) {
569
+ attr++;
570
+ oj_dump_cstr(attr, strlen(attr), 0, 0, out);
571
+ } else {
572
+ char buf[32];
573
+
574
+ *buf = '~';
575
+ strncpy(buf + 1, attr, sizeof(buf) - 2);
576
+ buf[sizeof(buf) - 1] = '\0';
577
+ oj_dump_cstr(buf, strlen(buf), 0, 0, out);
578
+ }
579
+ *out->cur++ = ':';
580
+ oj_dump_custom_val(value, depth, out, true);
581
+ out->depth = depth;
582
+ *out->cur++ = ',';
583
+
584
+ return ST_CONTINUE;
585
+ }
586
+
587
+ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
588
+ size_t size = 0;
589
+ int d2 = depth + 1;
590
+ int cnt;
591
+ bool class_written = false;
592
+
593
+ assure_size(out, 2);
594
+ *out->cur++ = '{';
595
+ if (Qundef != clas && NULL != out->opts->create_id && Yes == out->opts->create_ok) {
596
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
597
+ const char *classname = rb_obj_classname(obj);
598
+ size_t len = strlen(classname);
599
+
600
+ size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
601
+ assure_size(out, size);
602
+ fill_indent(out, d2);
603
+ *out->cur++ = '"';
604
+ APPEND_CHARS(out->cur, out->opts->create_id, out->opts->create_id_len);
605
+ *out->cur++ = '"';
606
+ if (0 < out->opts->dump_opts.before_size) {
607
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
608
+ }
609
+ *out->cur++ = ':';
610
+ if (0 < out->opts->dump_opts.after_size) {
611
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
612
+ }
613
+ *out->cur++ = '"';
614
+ APPEND_CHARS(out->cur, classname, len);
615
+ *out->cur++ = '"';
616
+ class_written = true;
617
+ }
618
+ cnt = (int)rb_ivar_count(obj);
619
+ if (class_written) {
620
+ *out->cur++ = ',';
621
+ }
622
+ if (0 == cnt && Qundef == clas) {
623
+ // Might be something special like an Enumerable.
624
+ if (Qtrue == rb_obj_is_kind_of(obj, oj_enumerable_class)) {
625
+ out->cur--;
626
+ oj_dump_custom_val(rb_funcall(obj, rb_intern("entries"), 0), depth, out, false);
627
+ return;
628
+ }
629
+ }
630
+ out->depth = depth + 1;
631
+ rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
632
+ if (',' == *(out->cur - 1)) {
633
+ out->cur--; // backup to overwrite last comma
634
+ }
635
+ if (rb_obj_is_kind_of(obj, rb_eException)) {
636
+ volatile VALUE rv;
637
+
638
+ if (',' != *(out->cur - 1)) {
639
+ *out->cur++ = ',';
640
+ }
641
+ // message
642
+ assure_size(out, 2);
643
+ fill_indent(out, d2);
644
+ oj_dump_cstr("~mesg", 5, 0, 0, out);
645
+ *out->cur++ = ':';
646
+ rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
647
+ oj_dump_custom_val(rv, d2, out, true);
648
+ assure_size(out, size + 2);
649
+ *out->cur++ = ',';
650
+ // backtrace
651
+ fill_indent(out, d2);
652
+ oj_dump_cstr("~bt", 3, 0, 0, out);
653
+ *out->cur++ = ':';
654
+ rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
655
+ oj_dump_custom_val(rv, d2, out, true);
656
+ assure_size(out, 2);
657
+ }
658
+ out->depth = depth;
659
+
660
+ fill_indent(out, depth);
661
+ *out->cur++ = '}';
662
+ *out->cur = '\0';
663
+ }
664
+
665
+ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
666
+ long id = oj_check_circular(obj, out);
667
+ VALUE clas;
668
+
669
+ if (0 > id) {
670
+ oj_dump_nil(Qnil, depth, out, false);
671
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
672
+ dump_obj_attrs(obj, clas, 0, depth, out);
673
+ }
674
+ *out->cur = '\0';
675
+ }
676
+
677
+ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
678
+ size_t size;
679
+ size_t i;
680
+ size_t cnt;
681
+ int d2 = depth + 1;
682
+ long id = oj_check_circular(a, out);
683
+
684
+ if (0 > id) {
685
+ oj_dump_nil(Qnil, depth, out, false);
686
+ return;
687
+ }
688
+ cnt = RARRAY_LEN(a);
689
+ *out->cur++ = '[';
690
+ assure_size(out, 2);
691
+ if (0 == cnt) {
692
+ *out->cur++ = ']';
693
+ } else {
694
+ if (out->opts->dump_opts.use) {
695
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
696
+ } else {
697
+ size = d2 * out->indent + 2;
698
+ }
699
+ assure_size(out, size * cnt);
700
+ cnt--;
701
+ for (i = 0; i <= cnt; i++) {
702
+ if (out->opts->dump_opts.use) {
703
+ if (0 < out->opts->dump_opts.array_size) {
704
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
705
+ }
706
+ if (0 < out->opts->dump_opts.indent_size) {
707
+ int i;
708
+ for (i = d2; 0 < i; i--) {
709
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
710
+ }
711
+ }
712
+ } else {
713
+ fill_indent(out, d2);
714
+ }
715
+ oj_dump_custom_val(RARRAY_AREF(a, i), d2, out, true);
716
+ if (i < cnt) {
717
+ *out->cur++ = ',';
718
+ }
719
+ }
720
+ size = depth * out->indent + 1;
721
+ assure_size(out, size);
722
+ if (out->opts->dump_opts.use) {
723
+ if (0 < out->opts->dump_opts.array_size) {
724
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
725
+ }
726
+ if (0 < out->opts->dump_opts.indent_size) {
727
+ int i;
728
+
729
+ for (i = depth; 0 < i; i--) {
730
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
731
+ }
732
+ }
733
+ } else {
734
+ fill_indent(out, depth);
735
+ }
736
+ *out->cur++ = ']';
737
+ }
738
+ *out->cur = '\0';
739
+ }
740
+
741
+ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
742
+ long id = oj_check_circular(obj, out);
743
+ VALUE clas;
744
+
745
+ if (0 > id) {
746
+ oj_dump_nil(Qnil, depth, out, false);
747
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
748
+ VALUE ma = Qnil;
749
+ VALUE v;
750
+ char num_id[32];
751
+ int i;
752
+ int d2 = depth + 1;
753
+ int d3 = d2 + 1;
754
+ size_t size = d2 * out->indent + d3 * out->indent + 3;
755
+ const char *name;
756
+ int cnt;
757
+ size_t len;
758
+
759
+ assure_size(out, size);
760
+ if (clas == rb_cRange) {
761
+ *out->cur++ = '"';
762
+ oj_dump_custom_val(rb_funcall(obj, oj_begin_id, 0), d3, out, false);
763
+ assure_size(out, 3);
764
+ APPEND_CHARS(out->cur, "..", 2);
765
+ if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
766
+ *out->cur++ = '.';
767
+ }
768
+ oj_dump_custom_val(rb_funcall(obj, oj_end_id, 0), d3, out, false);
769
+ *out->cur++ = '"';
770
+
771
+ return;
772
+ }
773
+ *out->cur++ = '{';
774
+ fill_indent(out, d2);
775
+ size = d3 * out->indent + 2;
776
+ ma = rb_struct_s_members(clas);
777
+
778
+ #ifdef RSTRUCT_LEN
779
+ #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
780
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
781
+ #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
782
+ cnt = (int)RSTRUCT_LEN(obj);
783
+ #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
784
+ #else
785
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
786
+ // class in interpreted Ruby so length() may not be defined.
787
+ cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
788
+ #endif
789
+ for (i = 0; i < cnt; i++) {
790
+ #ifdef RSTRUCT_LEN
791
+ v = RSTRUCT_GET(obj, i);
792
+ #else
793
+ v = rb_struct_aref(obj, INT2FIX(i));
794
+ #endif
795
+ if (ma != Qnil) {
796
+ volatile VALUE s = rb_sym2str(RARRAY_AREF(ma, i));
797
+
798
+ name = RSTRING_PTR(s);
799
+ len = RSTRING_LEN(s);
800
+ } else {
801
+ len = snprintf(num_id, sizeof(num_id), "%d", i);
802
+ name = num_id;
803
+ }
804
+ assure_size(out, size + len + 3);
805
+ fill_indent(out, d3);
806
+ *out->cur++ = '"';
807
+ APPEND_CHARS(out->cur, name, len);
808
+ APPEND_CHARS(out->cur, "\":", 2);
809
+ oj_dump_custom_val(v, d3, out, true);
810
+ *out->cur++ = ',';
811
+ }
812
+ out->cur--;
813
+ *out->cur++ = '}';
814
+ *out->cur = '\0';
815
+ }
816
+ }
817
+
818
+ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
819
+ long id = oj_check_circular(obj, out);
820
+ VALUE clas;
821
+
822
+ if (0 > id) {
823
+ oj_dump_nil(Qnil, depth, out, false);
824
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
825
+ dump_obj_attrs(obj, clas, id, depth, out);
826
+ }
827
+ }
828
+
829
+ static void dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
830
+ if (NULL != out->opts->create_id) {
831
+ dump_obj_str(obj, depth, out);
832
+ } else {
833
+ dump_obj_as_str(obj, depth, out);
834
+ }
835
+ }
836
+
837
+ static void dump_complex(VALUE obj, int depth, Out out, bool as_ok) {
838
+ complex_dump(obj, depth, out);
839
+ }
840
+
841
+ static void dump_rational(VALUE obj, int depth, Out out, bool as_ok) {
842
+ rational_dump(obj, depth, out);
843
+ }
844
+
845
+ static DumpFunc custom_funcs[] = {
846
+ NULL, // RUBY_T_NONE = 0x00,
847
+ dump_obj, // RUBY_T_OBJECT = 0x01,
848
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
849
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
850
+ oj_dump_float, // RUBY_T_FLOAT = 0x04,
851
+ oj_dump_str, // RUBY_T_STRING = 0x05,
852
+ dump_regexp, // RUBY_T_REGEXP = 0x06,
853
+ dump_array, // RUBY_T_ARRAY = 0x07,
854
+ dump_hash, // RUBY_T_HASH = 0x08,
855
+ dump_struct, // RUBY_T_STRUCT = 0x09,
856
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
857
+ NULL, // RUBY_T_FILE = 0x0b,
858
+ dump_data, // RUBY_T_DATA = 0x0c,
859
+ NULL, // RUBY_T_MATCH = 0x0d,
860
+ dump_complex, // RUBY_T_COMPLEX = 0x0e,
861
+ dump_rational, // RUBY_T_RATIONAL = 0x0f,
862
+ NULL, // 0x10
863
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
864
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
865
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
866
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
867
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
868
+ };
869
+
870
+ void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
871
+ int type = rb_type(obj);
872
+
873
+ TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
874
+ if (MAX_DEPTH < depth) {
875
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
876
+ }
877
+ if (0 < type && type <= RUBY_T_FIXNUM) {
878
+ DumpFunc f = custom_funcs[type];
879
+
880
+ if (NULL != f) {
881
+ f(obj, depth, out, true);
882
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
883
+ return;
884
+ }
885
+ }
886
+ oj_dump_nil(Qnil, depth, out, false);
887
+ TRACE(out->opts->trace, "dump", Qnil, depth, TraceOut);
888
+ }
889
+
890
+ ///// load functions /////
891
+
892
+ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
893
+ const char *key = kval->key;
894
+ int klen = kval->klen;
895
+ Val parent = stack_peek(&pi->stack);
896
+
897
+ if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
898
+ *pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
899
+ 0 == strncmp(pi->options.create_id, key, klen)) {
900
+ parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
901
+ if (2 == klen && '^' == *key && 'o' == key[1]) {
902
+ if (Qundef != parent->clas) {
903
+ if (!oj_code_has(codes, parent->clas, false)) {
904
+ parent->val = rb_obj_alloc(parent->clas);
905
+ }
906
+ }
907
+ }
908
+ } else {
909
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
910
+ volatile VALUE rkey = oj_calc_hash_key(pi, kval);
911
+
912
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
913
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
914
+
915
+ if (Qnil != clas) {
916
+ rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
917
+ }
918
+ }
919
+ switch (rb_type(parent->val)) {
920
+ case T_OBJECT: oj_set_obj_ivar(parent, kval, rstr); break;
921
+ case T_HASH:
922
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas &&
923
+ 0 == strncmp("time", parent->key, 4)) {
924
+ if (Qnil == (parent->val = oj_parse_xml_time(str, (int)len))) {
925
+ parent->val = rb_funcall(rb_cTime, rb_intern("parse"), 1, rb_str_new(str, len));
926
+ }
927
+ } else {
928
+ rb_hash_aset(parent->val, rkey, rstr);
929
+ }
930
+ break;
931
+ default: break;
932
+ }
933
+ TRACE_PARSE_CALL(pi->options.trace, "set_string", pi, rstr);
934
+ }
935
+ }
936
+
937
+ static void end_hash(struct _parseInfo *pi) {
938
+ Val parent = stack_peek(&pi->stack);
939
+
940
+ if (Qundef != parent->clas && parent->clas != rb_obj_class(parent->val)) {
941
+ volatile VALUE obj = oj_code_load(codes, parent->clas, parent->val);
942
+
943
+ if (Qnil != obj) {
944
+ parent->val = obj;
945
+ } else {
946
+ parent->val = rb_funcall(parent->clas, oj_json_create_id, 1, parent->val);
947
+ }
948
+ parent->clas = Qundef;
949
+ }
950
+ TRACE_PARSE_HASH_END(pi->options.trace, pi);
951
+ }
952
+
953
+ static void hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
954
+ Val parent = stack_peek(&pi->stack);
955
+ volatile VALUE rval = oj_num_as_value(ni);
956
+
957
+ switch (rb_type(parent->val)) {
958
+ case T_OBJECT: oj_set_obj_ivar(parent, kval, rval); break;
959
+ case T_HASH:
960
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 != ni->div &&
961
+ 0 == strncmp("time", parent->key, 4)) {
962
+ int64_t nsec = ni->num * 1000000000LL / ni->div;
963
+
964
+ if (ni->neg) {
965
+ ni->i = -ni->i;
966
+ if (0 < nsec) {
967
+ ni->i--;
968
+ nsec = 1000000000LL - nsec;
969
+ }
970
+ }
971
+ if (86400 == ni->exp) { // UTC time
972
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
973
+ // Since the ruby C routines always create local time, the
974
+ // offset and then a conversion to UTC keeps makes the time
975
+ // match the expected value.
976
+ parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
977
+ } else if (ni->has_exp) {
978
+ struct timespec ts;
979
+ ts.tv_sec = ni->i;
980
+ ts.tv_nsec = nsec;
981
+ parent->val = rb_time_timespec_new(&ts, (int)ni->exp);
982
+ } else {
983
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
984
+ }
985
+ rval = parent->val;
986
+ } else {
987
+ rb_hash_aset(parent->val, oj_calc_hash_key(pi, kval), rval);
988
+ }
989
+ break;
990
+ default: break;
991
+ }
992
+ TRACE_PARSE_CALL(pi->options.trace, "set_string", pi, rval);
993
+ }
994
+
995
+ static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
996
+ Val parent = stack_peek(&pi->stack);
997
+
998
+ switch (rb_type(parent->val)) {
999
+ case T_OBJECT: oj_set_obj_ivar(parent, kval, value); break;
1000
+ case T_HASH: rb_hash_aset(parent->val, oj_calc_hash_key(pi, kval), value); break;
1001
+ default: break;
1002
+ }
1003
+ TRACE_PARSE_CALL(pi->options.trace, "set_value", pi, value);
1004
+ }
1005
+
1006
+ static void array_append_num(ParseInfo pi, NumInfo ni) {
1007
+ Val parent = stack_peek(&pi->stack);
1008
+ volatile VALUE rval = oj_num_as_value(ni);
1009
+
1010
+ rb_ary_push(parent->val, rval);
1011
+ TRACE_PARSE_CALL(pi->options.trace, "append_number", pi, rval);
1012
+ }
1013
+
1014
+ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
1015
+ volatile VALUE rstr = rb_utf8_str_new(str, len);
1016
+
1017
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
1018
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
1019
+
1020
+ if (Qnil != clas) {
1021
+ rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
1022
+ return;
1023
+ }
1024
+ }
1025
+ rb_ary_push(stack_peek(&pi->stack)->val, rstr);
1026
+ TRACE_PARSE_CALL(pi->options.trace, "append_string", pi, rstr);
1027
+ }
1028
+
1029
+ void oj_set_custom_callbacks(ParseInfo pi) {
1030
+ oj_set_compat_callbacks(pi);
1031
+ pi->hash_set_cstr = hash_set_cstr;
1032
+ pi->end_hash = end_hash;
1033
+ pi->hash_set_num = hash_set_num;
1034
+ pi->hash_set_value = hash_set_value;
1035
+ pi->array_append_cstr = array_append_cstr;
1036
+ pi->array_append_num = array_append_num;
1037
+ }
1038
+
1039
+ VALUE
1040
+ oj_custom_parse(int argc, VALUE *argv, VALUE self) {
1041
+ struct _parseInfo pi;
1042
+
1043
+ parse_info_init(&pi);
1044
+ pi.options = oj_default_options;
1045
+ pi.handler = Qnil;
1046
+ pi.err_class = Qnil;
1047
+ pi.max_depth = 0;
1048
+ pi.options.allow_nan = Yes;
1049
+ pi.options.nilnil = Yes;
1050
+ oj_set_custom_callbacks(&pi);
1051
+
1052
+ if (T_STRING == rb_type(*argv)) {
1053
+ return oj_pi_parse(argc, argv, &pi, 0, 0, false);
1054
+ } else {
1055
+ return oj_pi_sparse(argc, argv, &pi, 0);
1056
+ }
1057
+ }
1058
+
1059
+ VALUE
1060
+ oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
1061
+ struct _parseInfo pi;
1062
+
1063
+ parse_info_init(&pi);
1064
+ pi.options = oj_default_options;
1065
+ pi.handler = Qnil;
1066
+ pi.err_class = Qnil;
1067
+ pi.max_depth = 0;
1068
+ pi.options.allow_nan = Yes;
1069
+ pi.options.nilnil = Yes;
1070
+ oj_set_custom_callbacks(&pi);
1071
+ pi.end_hash = end_hash;
1072
+
1073
+ return oj_pi_parse(argc, argv, &pi, json, len, false);
1074
+ }