oj 2.18.3 → 3.13.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1324 -0
  3. data/README.md +51 -204
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +49 -72
  6. data/ext/oj/cache.c +326 -0
  7. data/ext/oj/cache.h +21 -0
  8. data/ext/oj/cache8.c +61 -64
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +37 -68
  11. data/ext/oj/circarray.h +16 -42
  12. data/ext/oj/code.c +221 -0
  13. data/ext/oj/code.h +40 -0
  14. data/ext/oj/compat.c +231 -107
  15. data/ext/oj/custom.c +1125 -0
  16. data/ext/oj/debug.c +132 -0
  17. data/ext/oj/dump.c +935 -2513
  18. data/ext/oj/dump.h +108 -0
  19. data/ext/oj/dump_compat.c +936 -0
  20. data/ext/oj/dump_leaf.c +164 -0
  21. data/ext/oj/dump_object.c +761 -0
  22. data/ext/oj/dump_strict.c +410 -0
  23. data/ext/oj/encode.h +7 -42
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +40 -54
  26. data/ext/oj/err.h +52 -46
  27. data/ext/oj/extconf.rb +21 -30
  28. data/ext/oj/fast.c +1097 -1080
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +893 -0
  32. data/ext/oj/object.c +549 -620
  33. data/ext/oj/odd.c +155 -167
  34. data/ext/oj/odd.h +37 -63
  35. data/ext/oj/oj.c +1661 -2063
  36. data/ext/oj/oj.h +341 -270
  37. data/ext/oj/parse.c +974 -737
  38. data/ext/oj/parse.h +105 -97
  39. data/ext/oj/parser.c +1526 -0
  40. data/ext/oj/parser.h +90 -0
  41. data/ext/oj/rails.c +1504 -0
  42. data/ext/oj/rails.h +18 -0
  43. data/ext/oj/reader.c +141 -163
  44. data/ext/oj/reader.h +75 -113
  45. data/ext/oj/resolve.c +45 -93
  46. data/ext/oj/resolve.h +7 -34
  47. data/ext/oj/rxclass.c +143 -0
  48. data/ext/oj/rxclass.h +26 -0
  49. data/ext/oj/saj.c +447 -511
  50. data/ext/oj/saj2.c +348 -0
  51. data/ext/oj/scp.c +91 -138
  52. data/ext/oj/sparse.c +793 -644
  53. data/ext/oj/stream_writer.c +331 -0
  54. data/ext/oj/strict.c +145 -109
  55. data/ext/oj/string_writer.c +493 -0
  56. data/ext/oj/trace.c +72 -0
  57. data/ext/oj/trace.h +28 -0
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +62 -70
  62. data/ext/oj/val_stack.h +95 -129
  63. data/ext/oj/validate.c +51 -0
  64. data/ext/oj/wab.c +622 -0
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +17 -8
  67. data/lib/oj/error.rb +10 -11
  68. data/lib/oj/json.rb +176 -0
  69. data/lib/oj/mimic.rb +158 -19
  70. data/lib/oj/state.rb +132 -0
  71. data/lib/oj/version.rb +2 -2
  72. data/lib/oj.rb +1 -31
  73. data/pages/Advanced.md +22 -0
  74. data/pages/Compatibility.md +25 -0
  75. data/pages/Custom.md +23 -0
  76. data/pages/Encoding.md +65 -0
  77. data/pages/JsonGem.md +94 -0
  78. data/pages/Modes.md +161 -0
  79. data/pages/Options.md +327 -0
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +167 -0
  82. data/pages/Security.md +20 -0
  83. data/pages/WAB.md +13 -0
  84. data/test/activerecord/result_test.rb +32 -0
  85. data/test/activesupport4/decoding_test.rb +108 -0
  86. data/test/activesupport4/encoding_test.rb +531 -0
  87. data/test/activesupport4/test_helper.rb +41 -0
  88. data/test/activesupport5/abstract_unit.rb +45 -0
  89. data/test/activesupport5/decoding_test.rb +133 -0
  90. data/test/activesupport5/encoding_test.rb +500 -0
  91. data/test/activesupport5/encoding_test_cases.rb +98 -0
  92. data/test/activesupport5/test_helper.rb +72 -0
  93. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  94. data/test/activesupport6/abstract_unit.rb +44 -0
  95. data/test/activesupport6/decoding_test.rb +133 -0
  96. data/test/activesupport6/encoding_test.rb +507 -0
  97. data/test/activesupport6/encoding_test_cases.rb +98 -0
  98. data/test/activesupport6/test_common.rb +17 -0
  99. data/test/activesupport6/test_helper.rb +163 -0
  100. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  101. data/test/activesupport7/abstract_unit.rb +49 -0
  102. data/test/activesupport7/decoding_test.rb +125 -0
  103. data/test/activesupport7/encoding_test.rb +486 -0
  104. data/test/activesupport7/encoding_test_cases.rb +104 -0
  105. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  106. data/test/bar.rb +9 -0
  107. data/test/baz.rb +16 -0
  108. data/test/bug.rb +11 -46
  109. data/test/foo.rb +69 -16
  110. data/test/helper.rb +10 -1
  111. data/test/isolated/shared.rb +12 -8
  112. data/test/isolated/test_mimic_rails_after.rb +3 -3
  113. data/test/isolated/test_mimic_rails_before.rb +3 -3
  114. data/test/json_gem/json_addition_test.rb +216 -0
  115. data/test/json_gem/json_common_interface_test.rb +153 -0
  116. data/test/json_gem/json_encoding_test.rb +107 -0
  117. data/test/json_gem/json_ext_parser_test.rb +20 -0
  118. data/test/json_gem/json_fixtures_test.rb +35 -0
  119. data/test/json_gem/json_generator_test.rb +397 -0
  120. data/test/json_gem/json_generic_object_test.rb +90 -0
  121. data/test/json_gem/json_parser_test.rb +470 -0
  122. data/test/json_gem/json_string_matching_test.rb +42 -0
  123. data/test/json_gem/test_helper.rb +26 -0
  124. data/test/mem.rb +33 -0
  125. data/test/perf.rb +1 -1
  126. data/test/perf_compat.rb +30 -28
  127. data/test/perf_dump.rb +50 -0
  128. data/test/perf_object.rb +1 -1
  129. data/test/perf_once.rb +58 -0
  130. data/test/perf_parser.rb +189 -0
  131. data/test/perf_scp.rb +11 -10
  132. data/test/perf_strict.rb +30 -19
  133. data/test/perf_wab.rb +131 -0
  134. data/test/prec.rb +23 -0
  135. data/test/sample.rb +0 -1
  136. data/test/sample_json.rb +1 -1
  137. data/test/test_compat.rb +219 -102
  138. data/test/test_custom.rb +533 -0
  139. data/test/test_fast.rb +107 -35
  140. data/test/test_file.rb +19 -25
  141. data/test/test_generate.rb +21 -0
  142. data/test/test_hash.rb +11 -1
  143. data/test/test_integer_range.rb +72 -0
  144. data/test/test_null.rb +376 -0
  145. data/test/test_object.rb +357 -70
  146. data/test/test_parser.rb +27 -0
  147. data/test/test_parser_saj.rb +245 -0
  148. data/test/test_parser_usual.rb +217 -0
  149. data/test/test_rails.rb +35 -0
  150. data/test/test_saj.rb +1 -1
  151. data/test/test_scp.rb +39 -2
  152. data/test/test_strict.rb +186 -7
  153. data/test/test_various.rb +160 -774
  154. data/test/test_wab.rb +307 -0
  155. data/test/test_writer.rb +90 -2
  156. data/test/tests.rb +24 -0
  157. data/test/tests_mimic.rb +14 -0
  158. data/test/tests_mimic_addition.rb +7 -0
  159. data/test/zoo.rb +13 -0
  160. metadata +194 -56
  161. data/ext/oj/hash.c +0 -163
  162. data/ext/oj/hash.h +0 -46
  163. data/ext/oj/hash_test.c +0 -512
  164. data/test/activesupport_datetime_test.rb +0 -23
  165. data/test/bug2.rb +0 -10
  166. data/test/bug3.rb +0 -46
  167. data/test/bug_fast.rb +0 -32
  168. data/test/bug_load.rb +0 -24
  169. data/test/crash.rb +0 -111
  170. data/test/curl/curl_oj.rb +0 -46
  171. data/test/curl/get_oj.rb +0 -24
  172. data/test/curl/just_curl.rb +0 -31
  173. data/test/curl/just_oj.rb +0 -51
  174. data/test/example.rb +0 -11
  175. data/test/io.rb +0 -48
  176. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  177. data/test/mod.rb +0 -16
  178. data/test/rails.rb +0 -50
  179. data/test/russian.rb +0 -18
  180. data/test/struct.rb +0 -29
  181. data/test/test_serializer.rb +0 -59
  182. data/test/write_timebars.rb +0 -31
@@ -0,0 +1,761 @@
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 "dump.h"
5
+ #include "odd.h"
6
+ #include "trace.h"
7
+
8
+ static const char hex_chars[17] = "0123456789abcdef";
9
+
10
+ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
11
+
12
+ static void dump_time(VALUE obj, Out out) {
13
+ switch (out->opts->time_format) {
14
+ case RubyTime:
15
+ case XmlTime: oj_dump_xml_time(obj, out); break;
16
+ case UnixZTime: oj_dump_time(obj, out, 1); break;
17
+ case UnixTime:
18
+ default: oj_dump_time(obj, out, 0); break;
19
+ }
20
+ }
21
+
22
+ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
23
+ VALUE clas = rb_obj_class(obj);
24
+
25
+ if (rb_cTime == clas) {
26
+ assure_size(out, 6);
27
+ APPEND_CHARS(out->cur, "{\"^t\":", 6);
28
+ dump_time(obj, out);
29
+ *out->cur++ = '}';
30
+ *out->cur = '\0';
31
+ } else {
32
+ if (oj_bigdecimal_class == clas) {
33
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
34
+ const char * str = RSTRING_PTR(rstr);
35
+ int len = (int)RSTRING_LEN(rstr);
36
+
37
+ if (No != out->opts->bigdec_as_num) {
38
+ oj_dump_raw(str, len, out);
39
+ } else if (0 == strcasecmp("Infinity", str)) {
40
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
41
+ oj_dump_raw(str, len, out);
42
+ } else if (0 == strcasecmp("-Infinity", str)) {
43
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
44
+ oj_dump_raw(str, len, out);
45
+ } else {
46
+ oj_dump_cstr(str, len, 0, 0, out);
47
+ }
48
+ } else {
49
+ long id = oj_check_circular(obj, out);
50
+
51
+ if (0 <= id) {
52
+ dump_obj_attrs(obj, clas, id, depth, out);
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
59
+ VALUE clas = rb_obj_class(obj);
60
+
61
+ if (oj_bigdecimal_class == clas) {
62
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
63
+ const char * str = RSTRING_PTR(rstr);
64
+ int len = (int)RSTRING_LEN(rstr);
65
+
66
+ if (0 == strcasecmp("Infinity", str)) {
67
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
68
+ oj_dump_raw(str, len, out);
69
+ } else if (0 == strcasecmp("-Infinity", str)) {
70
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
71
+ oj_dump_raw(str, len, out);
72
+ } else {
73
+ oj_dump_raw(str, len, out);
74
+ }
75
+ } else {
76
+ long id = oj_check_circular(obj, out);
77
+
78
+ if (0 <= id) {
79
+ dump_obj_attrs(obj, clas, id, depth, out);
80
+ }
81
+ }
82
+ }
83
+
84
+ static void dump_class(VALUE obj, int depth, Out out, bool as_ok) {
85
+ const char *s = rb_class2name(obj);
86
+ size_t len = strlen(s);
87
+
88
+ assure_size(out, 6);
89
+ APPEND_CHARS(out->cur, "{\"^c\":", 6);
90
+ oj_dump_cstr(s, len, 0, 0, out);
91
+ *out->cur++ = '}';
92
+ *out->cur = '\0';
93
+ }
94
+
95
+ static void dump_array_class(VALUE a, VALUE clas, int depth, Out out) {
96
+ size_t size;
97
+ int i, cnt;
98
+ int d2 = depth + 1;
99
+ long id = oj_check_circular(a, out);
100
+
101
+ if (id < 0) {
102
+ return;
103
+ }
104
+ if (Qundef != clas && rb_cArray != clas && ObjectMode == out->opts->mode) {
105
+ dump_obj_attrs(a, clas, 0, depth, out);
106
+ return;
107
+ }
108
+ cnt = (int)RARRAY_LEN(a);
109
+ *out->cur++ = '[';
110
+ if (0 < id) {
111
+ assure_size(out, d2 * out->indent + 16);
112
+ fill_indent(out, d2);
113
+ APPEND_CHARS(out->cur, "\"^i", 3);
114
+ dump_ulong(id, out);
115
+ *out->cur++ = '"';
116
+ }
117
+ size = 2;
118
+ assure_size(out, 2);
119
+ if (0 == cnt) {
120
+ *out->cur++ = ']';
121
+ } else {
122
+ if (0 < id) {
123
+ *out->cur++ = ',';
124
+ }
125
+ if (out->opts->dump_opts.use) {
126
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
127
+ } else {
128
+ size = d2 * out->indent + 2;
129
+ }
130
+ assure_size(out, size * cnt);
131
+ cnt--;
132
+ for (i = 0; i <= cnt; i++) {
133
+ if (out->opts->dump_opts.use) {
134
+ if (0 < out->opts->dump_opts.array_size) {
135
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
136
+ }
137
+ if (0 < out->opts->dump_opts.indent_size) {
138
+ int i;
139
+ for (i = d2; 0 < i; i--) {
140
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
141
+ }
142
+ }
143
+ } else {
144
+ fill_indent(out, d2);
145
+ }
146
+ oj_dump_obj_val(RARRAY_AREF(a, i), d2, out);
147
+ if (i < cnt) {
148
+ *out->cur++ = ',';
149
+ }
150
+ }
151
+ size = depth * out->indent + 1;
152
+ assure_size(out, size);
153
+ if (out->opts->dump_opts.use) {
154
+ // printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size,
155
+ // out->opts->dump_opts->indent);
156
+ if (0 < out->opts->dump_opts.array_size) {
157
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
158
+ }
159
+ if (0 < out->opts->dump_opts.indent_size) {
160
+ int i;
161
+
162
+ for (i = depth; 0 < i; i--) {
163
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
164
+ }
165
+ }
166
+ } else {
167
+ fill_indent(out, depth);
168
+ }
169
+ *out->cur++ = ']';
170
+ }
171
+ *out->cur = '\0';
172
+ }
173
+
174
+ static void dump_array(VALUE obj, int depth, Out out, bool as_ok) {
175
+ dump_array_class(obj, rb_obj_class(obj), depth, out);
176
+ }
177
+
178
+ static void dump_str_class(VALUE obj, VALUE clas, int depth, Out out) {
179
+ if (Qundef != clas && rb_cString != clas) {
180
+ dump_obj_attrs(obj, clas, 0, depth, out);
181
+ } else {
182
+ const char *s = RSTRING_PTR(obj);
183
+ size_t len = (int)RSTRING_LEN(obj);
184
+ char s1 = s[1];
185
+
186
+ oj_dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
187
+ }
188
+ }
189
+
190
+ static void dump_str(VALUE obj, int depth, Out out, bool as_ok) {
191
+ dump_str_class(obj, rb_obj_class(obj), depth, out);
192
+ }
193
+
194
+ static void dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
195
+ volatile VALUE s = rb_sym2str(obj);
196
+
197
+ oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 1, 0, out);
198
+ }
199
+
200
+ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
201
+ Out out = (Out)ov;
202
+ int depth = out->depth;
203
+ long size = depth * out->indent + 1;
204
+
205
+ if (dump_ignore(out->opts, value)) {
206
+ return ST_CONTINUE;
207
+ }
208
+ if (out->omit_nil && Qnil == value) {
209
+ return ST_CONTINUE;
210
+ }
211
+ assure_size(out, size);
212
+ fill_indent(out, depth);
213
+ switch (rb_type(key)) {
214
+ case T_STRING:
215
+ dump_str_class(key, Qundef, depth, out);
216
+ *out->cur++ = ':';
217
+ oj_dump_obj_val(value, depth, out);
218
+ break;
219
+
220
+ case T_SYMBOL:
221
+ dump_sym(key, 0, out, false);
222
+ *out->cur++ = ':';
223
+ oj_dump_obj_val(value, depth, out);
224
+ break;
225
+
226
+ default:
227
+ {
228
+ int d2 = depth + 1;
229
+ long s2 = size + out->indent + 1;
230
+ int i;
231
+ int started = 0;
232
+ uint8_t b;
233
+
234
+ assure_size(out, s2 + 15);
235
+ APPEND_CHARS(out->cur, "\"^#", 3);
236
+ out->hash_cnt++;
237
+ for (i = 28; 0 <= i; i -= 4) {
238
+ b = (uint8_t)((out->hash_cnt >> i) & 0x0000000F);
239
+ if ('\0' != b) {
240
+ started = 1;
241
+ }
242
+ if (started) {
243
+ *out->cur++ = hex_chars[b];
244
+ }
245
+ }
246
+ APPEND_CHARS(out->cur, "\":[", 3);
247
+ fill_indent(out, d2);
248
+ oj_dump_obj_val(key, d2, out);
249
+ assure_size(out, s2);
250
+ *out->cur++ = ',';
251
+ fill_indent(out, d2);
252
+ oj_dump_obj_val(value, d2, out);
253
+ assure_size(out, size);
254
+ fill_indent(out, depth);
255
+ *out->cur++ = ']';
256
+ }
257
+ }
258
+ out->depth = depth;
259
+ *out->cur++ = ',';
260
+
261
+ return ST_CONTINUE;
262
+ }
263
+
264
+ static void dump_hash_class(VALUE obj, VALUE clas, int depth, Out out) {
265
+ int cnt;
266
+ size_t size;
267
+
268
+ if (Qundef != clas && rb_cHash != clas) {
269
+ dump_obj_attrs(obj, clas, 0, depth, out);
270
+ return;
271
+ }
272
+ cnt = (int)RHASH_SIZE(obj);
273
+ size = depth * out->indent + 2;
274
+ assure_size(out, 2);
275
+ if (0 == cnt) {
276
+ APPEND_CHARS(out->cur, "{}", 2);
277
+ } else {
278
+ long id = oj_check_circular(obj, out);
279
+
280
+ if (0 > id) {
281
+ return;
282
+ }
283
+ *out->cur++ = '{';
284
+ if (0 < id) {
285
+ assure_size(out, size + 16);
286
+ fill_indent(out, depth + 1);
287
+ APPEND_CHARS(out->cur, "\"^i\":", 5);
288
+ dump_ulong(id, out);
289
+ *out->cur++ = ',';
290
+ }
291
+ out->depth = depth + 1;
292
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
293
+ if (',' == *(out->cur - 1)) {
294
+ out->cur--; // backup to overwrite last comma
295
+ }
296
+ if (!out->opts->dump_opts.use) {
297
+ assure_size(out, size);
298
+ fill_indent(out, depth);
299
+ } else {
300
+ size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
301
+ assure_size(out, size);
302
+ if (0 < out->opts->dump_opts.hash_size) {
303
+ APPEND_CHARS(out->cur, out->opts->dump_opts.hash_nl, out->opts->dump_opts.hash_size);
304
+ }
305
+ if (0 < out->opts->dump_opts.indent_size) {
306
+ int i;
307
+
308
+ for (i = depth; 0 < i; i--) {
309
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
310
+ }
311
+ }
312
+ }
313
+ *out->cur++ = '}';
314
+ }
315
+ *out->cur = '\0';
316
+ }
317
+
318
+ #ifdef HAVE_RB_IVAR_FOREACH
319
+ static int dump_attr_cb(ID key, VALUE value, VALUE ov) {
320
+ Out out = (Out)ov;
321
+ int depth = out->depth;
322
+ size_t size = depth * out->indent + 1;
323
+ const char *attr = rb_id2name(key);
324
+
325
+ if (dump_ignore(out->opts, value)) {
326
+ return ST_CONTINUE;
327
+ }
328
+ if (out->omit_nil && Qnil == value) {
329
+ return ST_CONTINUE;
330
+ }
331
+ // Some exceptions such as NoMethodError have an invisible attribute where
332
+ // the key name is NULL. Not an empty string but NULL.
333
+ if (NULL == attr) {
334
+ attr = "";
335
+ } else if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
336
+ return ST_CONTINUE;
337
+ }
338
+ if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
339
+ return ST_CONTINUE;
340
+ }
341
+ assure_size(out, size);
342
+ fill_indent(out, depth);
343
+ if ('@' == *attr) {
344
+ attr++;
345
+ oj_dump_cstr(attr, strlen(attr), 0, 0, out);
346
+ } else {
347
+ char buf[32];
348
+
349
+ *buf = '~';
350
+ strncpy(buf + 1, attr, sizeof(buf) - 2);
351
+ buf[sizeof(buf) - 1] = '\0';
352
+ oj_dump_cstr(buf, strlen(buf), 0, 0, out);
353
+ }
354
+ *out->cur++ = ':';
355
+ oj_dump_obj_val(value, depth, out);
356
+ out->depth = depth;
357
+ *out->cur++ = ',';
358
+
359
+ return ST_CONTINUE;
360
+ }
361
+ #endif
362
+
363
+ static void dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
364
+ dump_hash_class(obj, rb_obj_class(obj), depth, out);
365
+ }
366
+
367
+ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
368
+ ID * idp;
369
+ AttrGetFunc * fp;
370
+ volatile VALUE v;
371
+ const char * name;
372
+ size_t size;
373
+ int d2 = depth + 1;
374
+
375
+ assure_size(out, 2);
376
+ *out->cur++ = '{';
377
+ if (Qundef != clas) {
378
+ const char *class_name = rb_class2name(clas);
379
+ int clen = (int)strlen(class_name);
380
+
381
+ size = d2 * out->indent + clen + 10;
382
+ assure_size(out, size);
383
+ fill_indent(out, d2);
384
+ APPEND_CHARS(out->cur, "\"^O\":", 5);
385
+ oj_dump_cstr(class_name, clen, 0, 0, out);
386
+ *out->cur++ = ',';
387
+ }
388
+ if (odd->raw) {
389
+ v = rb_funcall(obj, *odd->attrs, 0);
390
+ if (Qundef == v || T_STRING != rb_type(v)) {
391
+ rb_raise(rb_eEncodingError, "Invalid type for raw JSON.");
392
+ } else {
393
+ const char *s = RSTRING_PTR(v);
394
+ int len = (int)RSTRING_LEN(v);
395
+ const char *name = rb_id2name(*odd->attrs);
396
+ size_t nlen = strlen(name);
397
+
398
+ size = len + d2 * out->indent + nlen + 10;
399
+ assure_size(out, size);
400
+ fill_indent(out, d2);
401
+ *out->cur++ = '"';
402
+ APPEND_CHARS(out->cur, name, nlen);
403
+ APPEND_CHARS(out->cur, "\":", 2);
404
+ APPEND_CHARS(out->cur, s, len);
405
+ *out->cur = '\0';
406
+ }
407
+ } else {
408
+ size = d2 * out->indent + 1;
409
+ for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
410
+ size_t nlen;
411
+
412
+ assure_size(out, size);
413
+ name = rb_id2name(*idp);
414
+ nlen = strlen(name);
415
+ if (NULL != *fp) {
416
+ v = (*fp)(obj);
417
+ } else if (0 == strchr(name, '.')) {
418
+ v = rb_funcall(obj, *idp, 0);
419
+ } else {
420
+ char nbuf[256];
421
+ char *n2 = nbuf;
422
+ char *n;
423
+ char *end;
424
+ ID i;
425
+
426
+ if (sizeof(nbuf) <= nlen) {
427
+ if (NULL == (n2 = strdup(name))) {
428
+ rb_raise(rb_eNoMemError, "for attribute name.");
429
+ }
430
+ } else {
431
+ strcpy(n2, name);
432
+ }
433
+ n = n2;
434
+ v = obj;
435
+ while (0 != (end = strchr(n, '.'))) {
436
+ *end = '\0';
437
+ i = rb_intern(n);
438
+ v = rb_funcall(v, i, 0);
439
+ n = end + 1;
440
+ }
441
+ i = rb_intern(n);
442
+ v = rb_funcall(v, i, 0);
443
+ if (nbuf != n2) {
444
+ free(n2);
445
+ }
446
+ }
447
+ fill_indent(out, d2);
448
+ oj_dump_cstr(name, nlen, 0, 0, out);
449
+ *out->cur++ = ':';
450
+ oj_dump_obj_val(v, d2, out);
451
+ assure_size(out, 2);
452
+ *out->cur++ = ',';
453
+ }
454
+ out->cur--;
455
+ }
456
+ *out->cur++ = '}';
457
+ *out->cur = '\0';
458
+ }
459
+
460
+ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
461
+ size_t size = 0;
462
+ int d2 = depth + 1;
463
+ int type = rb_type(obj);
464
+ Odd odd;
465
+
466
+ if (0 != (odd = oj_get_odd(clas))) {
467
+ dump_odd(obj, odd, clas, depth + 1, out);
468
+ return;
469
+ }
470
+ assure_size(out, 2);
471
+ *out->cur++ = '{';
472
+ if (Qundef != clas) {
473
+ const char *class_name = rb_class2name(clas);
474
+ int clen = (int)strlen(class_name);
475
+
476
+ assure_size(out, d2 * out->indent + clen + 10);
477
+ fill_indent(out, d2);
478
+ APPEND_CHARS(out->cur, "\"^o\":", 5);
479
+ oj_dump_cstr(class_name, clen, 0, 0, out);
480
+ }
481
+ if (0 < id) {
482
+ assure_size(out, d2 * out->indent + 16);
483
+ *out->cur++ = ',';
484
+ fill_indent(out, d2);
485
+ APPEND_CHARS(out->cur, "\"^i\":", 5);
486
+ dump_ulong(id, out);
487
+ }
488
+ switch (type) {
489
+ case T_STRING:
490
+ assure_size(out, d2 * out->indent + 14);
491
+ *out->cur++ = ',';
492
+ fill_indent(out, d2);
493
+ APPEND_CHARS(out->cur, "\"self\":", 7);
494
+ oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
495
+ break;
496
+ case T_ARRAY:
497
+ assure_size(out, d2 * out->indent + 14);
498
+ *out->cur++ = ',';
499
+ fill_indent(out, d2);
500
+ APPEND_CHARS(out->cur, "\"self\":", 7);
501
+ dump_array_class(obj, Qundef, depth + 1, out);
502
+ break;
503
+ case T_HASH:
504
+ assure_size(out, d2 * out->indent + 14);
505
+ *out->cur++ = ',';
506
+ fill_indent(out, d2);
507
+ APPEND_CHARS(out->cur, "\"self\":", 7);
508
+ dump_hash_class(obj, Qundef, depth + 1, out);
509
+ break;
510
+ default: break;
511
+ }
512
+ {
513
+ int cnt;
514
+ #ifdef HAVE_RB_IVAR_COUNT
515
+ cnt = (int)rb_ivar_count(obj);
516
+ #else
517
+ volatile VALUE vars = rb_funcall2(obj, oj_instance_variables_id, 0, 0);
518
+ VALUE * np = RARRAY_PTR(vars);
519
+ ID vid;
520
+ const char * attr;
521
+ int i;
522
+ int first = 1;
523
+
524
+ cnt = (int)RARRAY_LEN(vars);
525
+ #endif
526
+ if (Qundef != clas && 0 < cnt) {
527
+ *out->cur++ = ',';
528
+ }
529
+ if (0 == cnt && Qundef == clas) {
530
+ // Might be something special like an Enumerable.
531
+ if (Qtrue == rb_obj_is_kind_of(obj, oj_enumerable_class)) {
532
+ out->cur--;
533
+ oj_dump_obj_val(rb_funcall(obj, rb_intern("entries"), 0), depth, out);
534
+ return;
535
+ }
536
+ }
537
+ out->depth = depth + 1;
538
+ #ifdef HAVE_RB_IVAR_FOREACH
539
+ rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
540
+ if (',' == *(out->cur - 1)) {
541
+ out->cur--; // backup to overwrite last comma
542
+ }
543
+ #else
544
+ size = d2 * out->indent + 1;
545
+ for (i = cnt; 0 < i; i--, np++) {
546
+ VALUE value;
547
+
548
+ vid = rb_to_id(*np);
549
+ attr = rb_id2name(vid);
550
+ if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
551
+ continue;
552
+ }
553
+ value = rb_ivar_get(obj, vid);
554
+
555
+ if (dump_ignore(out->opts, value)) {
556
+ continue;
557
+ }
558
+ if (out->omit_nil && Qnil == value) {
559
+ continue;
560
+ }
561
+ if (first) {
562
+ first = 0;
563
+ } else {
564
+ *out->cur++ = ',';
565
+ }
566
+ assure_size(out, size);
567
+ fill_indent(out, d2);
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(attr) + 1, 0, 0, out);
578
+ }
579
+ *out->cur++ = ':';
580
+ oj_dump_obj_val(value, d2, out);
581
+ assure_size(out, 2);
582
+ }
583
+ #endif
584
+ if (rb_obj_is_kind_of(obj, rb_eException)) {
585
+ volatile VALUE rv;
586
+
587
+ if (',' != *(out->cur - 1)) {
588
+ *out->cur++ = ',';
589
+ }
590
+ // message
591
+ assure_size(out, size);
592
+ fill_indent(out, d2);
593
+ oj_dump_cstr("~mesg", 5, 0, 0, out);
594
+ *out->cur++ = ':';
595
+ rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
596
+ oj_dump_obj_val(rv, d2, out);
597
+ assure_size(out, 2);
598
+ *out->cur++ = ',';
599
+ // backtrace
600
+ assure_size(out, size);
601
+ fill_indent(out, d2);
602
+ oj_dump_cstr("~bt", 3, 0, 0, out);
603
+ *out->cur++ = ':';
604
+ rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
605
+ oj_dump_obj_val(rv, d2, out);
606
+ assure_size(out, 2);
607
+ }
608
+ out->depth = depth;
609
+ }
610
+ fill_indent(out, depth);
611
+ *out->cur++ = '}';
612
+ *out->cur = '\0';
613
+ }
614
+
615
+ static void dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
616
+ dump_obj_attrs(obj, rb_obj_class(obj), 0, depth, out);
617
+ }
618
+
619
+ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
620
+ VALUE clas = rb_obj_class(obj);
621
+ const char *class_name = rb_class2name(clas);
622
+ int i;
623
+ int d2 = depth + 1;
624
+ int d3 = d2 + 1;
625
+ size_t len = strlen(class_name);
626
+ size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
627
+
628
+ assure_size(out, size);
629
+ *out->cur++ = '{';
630
+ fill_indent(out, d2);
631
+ APPEND_CHARS(out->cur, "\"^u\":[", 6);
632
+ if ('#' == *class_name) {
633
+ VALUE ma = rb_struct_s_members(clas);
634
+ const char *name;
635
+ int cnt = (int)RARRAY_LEN(ma);
636
+
637
+ *out->cur++ = '[';
638
+ for (i = 0; i < cnt; i++) {
639
+ volatile VALUE s = rb_sym2str(RARRAY_AREF(ma, i));
640
+
641
+ name = RSTRING_PTR(s);
642
+ len = (int)RSTRING_LEN(s);
643
+ size = len + 3;
644
+ assure_size(out, size);
645
+ if (0 < i) {
646
+ *out->cur++ = ',';
647
+ }
648
+ *out->cur++ = '"';
649
+ APPEND_CHARS(out->cur, name, len);
650
+ *out->cur++ = '"';
651
+ }
652
+ *out->cur++ = ']';
653
+ } else {
654
+ fill_indent(out, d3);
655
+ *out->cur++ = '"';
656
+ APPEND_CHARS(out->cur, class_name, len);
657
+ *out->cur++ = '"';
658
+ }
659
+ *out->cur++ = ',';
660
+ size = d3 * out->indent + 2;
661
+ #ifdef RSTRUCT_LEN
662
+ {
663
+ VALUE v;
664
+ int cnt;
665
+ #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
666
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
667
+ #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
668
+ cnt = (int)RSTRUCT_LEN(obj);
669
+ #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
670
+
671
+ for (i = 0; i < cnt; i++) {
672
+ v = RSTRUCT_GET(obj, i);
673
+ if (dump_ignore(out->opts, v)) {
674
+ v = Qnil;
675
+ }
676
+ assure_size(out, size);
677
+ fill_indent(out, d3);
678
+ oj_dump_obj_val(v, d3, out);
679
+ *out->cur++ = ',';
680
+ }
681
+ }
682
+ #else
683
+ {
684
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
685
+ // class in interpreted Ruby so length() may not be defined.
686
+ int slen = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
687
+
688
+ for (i = 0; i < slen; i++) {
689
+ assure_size(out, size);
690
+ fill_indent(out, d3);
691
+ if (dump_ignore(out->opts, v)) {
692
+ v = Qnil;
693
+ }
694
+ oj_dump_obj_val(rb_struct_aref(obj, INT2FIX(i)), d3, out, 0, 0, true);
695
+ *out->cur++ = ',';
696
+ }
697
+ }
698
+ #endif
699
+ out->cur--;
700
+ APPEND_CHARS(out->cur, "]}", 2);
701
+ *out->cur = '\0';
702
+ }
703
+
704
+ static void dump_complex(VALUE obj, int depth, Out out, bool as_ok) {
705
+ dump_obj_attrs(obj, rb_obj_class(obj), 0, depth, out);
706
+ }
707
+
708
+ static void dump_rational(VALUE obj, int depth, Out out, bool as_ok) {
709
+ dump_obj_attrs(obj, rb_obj_class(obj), 0, depth, out);
710
+ }
711
+
712
+ static DumpFunc obj_funcs[] = {
713
+ NULL, // RUBY_T_NONE = 0x00,
714
+ dump_obj, // RUBY_T_OBJECT = 0x01,
715
+ dump_class, // RUBY_T_CLASS = 0x02,
716
+ dump_class, // RUBY_T_MODULE = 0x03,
717
+ oj_dump_float, // RUBY_T_FLOAT = 0x04,
718
+ dump_str, // RUBY_T_STRING = 0x05,
719
+ dump_regexp, // RUBY_T_REGEXP = 0x06,
720
+ dump_array, // RUBY_T_ARRAY = 0x07,
721
+ dump_hash, // RUBY_T_HASH = 0x08,
722
+ dump_struct, // RUBY_T_STRUCT = 0x09,
723
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
724
+ NULL, // RUBY_T_FILE = 0x0b,
725
+ dump_data, // RUBY_T_DATA = 0x0c,
726
+ NULL, // RUBY_T_MATCH = 0x0d,
727
+ dump_complex, // RUBY_T_COMPLEX = 0x0e,
728
+ dump_rational, // RUBY_T_RATIONAL = 0x0f,
729
+ NULL, // 0x10
730
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
731
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
732
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
733
+ dump_sym, // RUBY_T_SYMBOL = 0x14,
734
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
735
+ };
736
+
737
+ void oj_dump_obj_val(VALUE obj, int depth, Out out) {
738
+ int type = rb_type(obj);
739
+
740
+ if (Yes == out->opts->trace) {
741
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
742
+ }
743
+ if (MAX_DEPTH < depth) {
744
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
745
+ }
746
+ if (0 < type && type <= RUBY_T_FIXNUM) {
747
+ DumpFunc f = obj_funcs[type];
748
+
749
+ if (NULL != f) {
750
+ f(obj, depth, out, false);
751
+ if (Yes == out->opts->trace) {
752
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
753
+ }
754
+ return;
755
+ }
756
+ }
757
+ oj_dump_nil(Qnil, depth, out, false);
758
+ if (Yes == out->opts->trace) {
759
+ oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
760
+ }
761
+ }