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
@@ -0,0 +1,897 @@
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 "code.h"
5
+ #include "dump.h"
6
+ #include "rails.h"
7
+ #include "trace.h"
8
+
9
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
10
+ #define OJ_INFINITY (1.0 / 0.0)
11
+
12
+ bool oj_use_hash_alt = false;
13
+ bool oj_use_array_alt = false;
14
+
15
+ static bool use_struct_alt = false;
16
+ static bool use_exception_alt = false;
17
+ static bool use_bignum_alt = false;
18
+
19
+ static void raise_json_err(const char *msg, const char *err_classname) {
20
+ rb_raise(oj_get_json_err_class(err_classname), "%s", msg);
21
+ }
22
+
23
+ static void dump_obj_classname(const char *classname, int depth, Out out) {
24
+ int d2 = depth + 1;
25
+ size_t len = strlen(classname);
26
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
27
+ size_t size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
28
+
29
+ assure_size(out, size);
30
+ *out->cur++ = '{';
31
+ fill_indent(out, d2);
32
+ *out->cur++ = '"';
33
+ APPEND_CHARS(out->cur, out->opts->create_id, out->opts->create_id_len);
34
+ *out->cur++ = '"';
35
+ if (0 < out->opts->dump_opts.before_size) {
36
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
37
+ }
38
+ *out->cur++ = ':';
39
+ if (0 < out->opts->dump_opts.after_size) {
40
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
41
+ }
42
+ *out->cur++ = '"';
43
+ APPEND_CHARS(out->cur, classname, len);
44
+ *out->cur++ = '"';
45
+ }
46
+
47
+ static void dump_values_array(VALUE *values, int depth, Out out) {
48
+ size_t size;
49
+ int d2 = depth + 1;
50
+
51
+ assure_size(out, d2 * out->indent + 3);
52
+ *out->cur++ = '[';
53
+ if (Qundef == *values) {
54
+ *out->cur++ = ']';
55
+ } else {
56
+ if (out->opts->dump_opts.use) {
57
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 2;
58
+ size += out->opts->dump_opts.array_size;
59
+ size += out->opts->dump_opts.indent_size;
60
+ } else {
61
+ size = d2 * out->indent + 3;
62
+ }
63
+ for (; Qundef != *values; values++) {
64
+ assure_size(out, size);
65
+ if (out->opts->dump_opts.use) {
66
+ if (0 < out->opts->dump_opts.array_size) {
67
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
68
+ }
69
+ if (0 < out->opts->dump_opts.indent_size) {
70
+ int i;
71
+
72
+ for (i = d2; 0 < i; i--) {
73
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
74
+ }
75
+ }
76
+ } else {
77
+ fill_indent(out, d2);
78
+ }
79
+ oj_dump_compat_val(*values, d2, out, true);
80
+ if (Qundef != *(values + 1)) {
81
+ *out->cur++ = ',';
82
+ }
83
+ }
84
+ assure_size(out, size);
85
+ if (out->opts->dump_opts.use) {
86
+ if (0 < out->opts->dump_opts.array_size) {
87
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
88
+ }
89
+ if (0 < out->opts->dump_opts.indent_size) {
90
+ int i;
91
+
92
+ for (i = depth; 0 < i; i--) {
93
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
94
+ }
95
+ }
96
+ } else {
97
+ fill_indent(out, depth);
98
+ }
99
+ *out->cur++ = ']';
100
+ }
101
+ }
102
+
103
+ static void dump_to_json(VALUE obj, Out out) {
104
+ volatile VALUE rs;
105
+ const char *s;
106
+ size_t len;
107
+
108
+ TRACE(out->opts->trace, "to_json", obj, 0, TraceRubyIn);
109
+ if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
110
+ rs = rb_funcall(obj, oj_to_json_id, 0);
111
+ } else {
112
+ rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
113
+ }
114
+ TRACE(out->opts->trace, "to_json", obj, 0, TraceRubyOut);
115
+
116
+ StringValue(rs);
117
+ s = RSTRING_PTR(rs);
118
+ len = RSTRING_LEN(rs);
119
+
120
+ assure_size(out, len + 1);
121
+ APPEND_CHARS(out->cur, s, len);
122
+ *out->cur = '\0';
123
+ }
124
+
125
+ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
126
+ size_t size;
127
+ size_t i;
128
+ size_t cnt;
129
+ int d2 = depth + 1;
130
+ long id = oj_check_circular(a, out);
131
+
132
+ if (0 > id) {
133
+ raise_json_err("Too deeply nested", "NestingError");
134
+ return;
135
+ }
136
+ if (as_ok && !oj_use_array_alt && rb_obj_class(a) != rb_cArray && rb_respond_to(a, oj_to_json_id)) {
137
+ dump_to_json(a, out);
138
+ return;
139
+ }
140
+ cnt = RARRAY_LEN(a);
141
+ *out->cur++ = '[';
142
+ assure_size(out, 2);
143
+ if (0 == cnt) {
144
+ *out->cur++ = ']';
145
+ } else {
146
+ if (out->opts->dump_opts.use) {
147
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
148
+ } else {
149
+ size = d2 * out->indent + 2;
150
+ }
151
+ assure_size(out, size * cnt);
152
+ cnt--;
153
+ for (i = 0; i <= cnt; i++) {
154
+ if (out->opts->dump_opts.use) {
155
+ if (0 < out->opts->dump_opts.array_size) {
156
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
157
+ }
158
+ if (0 < out->opts->dump_opts.indent_size) {
159
+ int i;
160
+ for (i = d2; 0 < i; i--) {
161
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
162
+ }
163
+ }
164
+ } else {
165
+ fill_indent(out, d2);
166
+ }
167
+ oj_dump_compat_val(RARRAY_AREF(a, i), d2, out, true);
168
+ if (i < cnt) {
169
+ *out->cur++ = ',';
170
+ }
171
+ }
172
+ if (out->opts->dump_opts.use) {
173
+ size = out->opts->dump_opts.array_size + out->opts->dump_opts.indent_size * depth + 1;
174
+ assure_size(out, size);
175
+ if (0 < out->opts->dump_opts.array_size) {
176
+ APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
177
+ }
178
+ if (0 < out->opts->dump_opts.indent_size) {
179
+ int i;
180
+
181
+ for (i = depth; 0 < i; i--) {
182
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
183
+ }
184
+ }
185
+ } else {
186
+ size = depth * out->indent + 1;
187
+ assure_size(out, size);
188
+ fill_indent(out, depth);
189
+ }
190
+ *out->cur++ = ']';
191
+ }
192
+ *out->cur = '\0';
193
+ }
194
+
195
+ static ID _dump_id = 0;
196
+
197
+ static void bigdecimal_alt(VALUE obj, int depth, Out out) {
198
+ struct _attr attrs[] = {
199
+ {"b", 1, Qnil},
200
+ {NULL, 0, Qnil},
201
+ };
202
+
203
+ if (0 == _dump_id) {
204
+ _dump_id = rb_intern("_dump");
205
+ }
206
+ attrs[0].value = rb_funcall(obj, _dump_id, 0);
207
+
208
+ oj_code_attrs(obj, attrs, depth, out, true);
209
+ }
210
+
211
+ static ID real_id = 0;
212
+ static ID imag_id = 0;
213
+
214
+ static void complex_alt(VALUE obj, int depth, Out out) {
215
+ struct _attr attrs[] = {
216
+ {"r", 1, Qnil},
217
+ {"i", 1, Qnil},
218
+ {NULL, 0, Qnil},
219
+ };
220
+
221
+ if (0 == real_id) {
222
+ real_id = rb_intern("real");
223
+ imag_id = rb_intern("imag");
224
+ }
225
+ attrs[0].value = rb_funcall(obj, real_id, 0);
226
+ attrs[1].value = rb_funcall(obj, imag_id, 0);
227
+
228
+ oj_code_attrs(obj, attrs, depth, out, true);
229
+ }
230
+
231
+ static ID year_id = 0;
232
+ static ID month_id = 0;
233
+ static ID day_id = 0;
234
+ static ID start_id = 0;
235
+
236
+ static void date_alt(VALUE obj, int depth, Out out) {
237
+ struct _attr attrs[] = {
238
+ {"y", 1, Qnil},
239
+ {"m", 1, Qnil},
240
+ {"d", 1, Qnil},
241
+ {"sg", 2, Qnil},
242
+ {NULL, 0, Qnil},
243
+ };
244
+ if (0 == year_id) {
245
+ year_id = rb_intern("year");
246
+ month_id = rb_intern("month");
247
+ day_id = rb_intern("day");
248
+ start_id = rb_intern("start");
249
+ }
250
+ attrs[0].value = rb_funcall(obj, year_id, 0);
251
+ attrs[1].value = rb_funcall(obj, month_id, 0);
252
+ attrs[2].value = rb_funcall(obj, day_id, 0);
253
+ attrs[3].value = rb_funcall(obj, start_id, 0);
254
+
255
+ oj_code_attrs(obj, attrs, depth, out, true);
256
+ }
257
+
258
+ static ID hour_id = 0;
259
+ static ID min_id = 0;
260
+ static ID sec_id = 0;
261
+ static ID offset_id = 0;
262
+
263
+ static void datetime_alt(VALUE obj, int depth, Out out) {
264
+ struct _attr attrs[] = {
265
+ {"y", 1, Qnil},
266
+ {"m", 1, Qnil},
267
+ {"d", 1, Qnil},
268
+ {"H", 1, Qnil},
269
+ {"M", 1, Qnil},
270
+ {"S", 1, Qnil},
271
+ {"of", 2, Qnil},
272
+ {"sg", 2, Qnil},
273
+ {NULL, 0, Qnil},
274
+ };
275
+ if (0 == hour_id) {
276
+ year_id = rb_intern("year");
277
+ month_id = rb_intern("month");
278
+ day_id = rb_intern("day");
279
+ hour_id = rb_intern("hour");
280
+ min_id = rb_intern("min");
281
+ sec_id = rb_intern("sec");
282
+ offset_id = rb_intern("offset");
283
+ start_id = rb_intern("start");
284
+ }
285
+ attrs[0].value = rb_funcall(obj, year_id, 0);
286
+ attrs[1].value = rb_funcall(obj, month_id, 0);
287
+ attrs[2].value = rb_funcall(obj, day_id, 0);
288
+ attrs[3].value = rb_funcall(obj, hour_id, 0);
289
+ attrs[4].value = rb_funcall(obj, min_id, 0);
290
+ attrs[5].value = rb_funcall(obj, sec_id, 0);
291
+ attrs[6].value = oj_safe_string_convert(rb_funcall(obj, offset_id, 0));
292
+ attrs[7].value = rb_funcall(obj, start_id, 0);
293
+
294
+ oj_code_attrs(obj, attrs, depth, out, true);
295
+ }
296
+
297
+ static ID message_id = 0;
298
+ static ID backtrace_id = 0;
299
+
300
+ static void exception_alt(VALUE obj, int depth, Out out) {
301
+ int d3 = depth + 2;
302
+ size_t size = d3 * out->indent + 2;
303
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
304
+
305
+ if (0 == message_id) {
306
+ message_id = rb_intern("message");
307
+ backtrace_id = rb_intern("backtrace");
308
+ }
309
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
310
+
311
+ assure_size(out, size + sep_len + 6);
312
+ *out->cur++ = ',';
313
+ fill_indent(out, d3);
314
+ APPEND_CHARS(out->cur, "\"m\"", 3);
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
+ oj_dump_str(rb_funcall(obj, message_id, 0), 0, out, false);
323
+ assure_size(out, size + sep_len + 6);
324
+ *out->cur++ = ',';
325
+ fill_indent(out, d3);
326
+ APPEND_CHARS(out->cur, "\"b\"", 3);
327
+ if (0 < out->opts->dump_opts.before_size) {
328
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
329
+ }
330
+ *out->cur++ = ':';
331
+ if (0 < out->opts->dump_opts.after_size) {
332
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
333
+ }
334
+ dump_array(rb_funcall(obj, backtrace_id, 0), depth, out, false);
335
+ fill_indent(out, depth);
336
+ *out->cur++ = '}';
337
+ *out->cur = '\0';
338
+ }
339
+
340
+ static ID table_id = 0;
341
+
342
+ static void openstruct_alt(VALUE obj, int depth, Out out) {
343
+ struct _attr attrs[] = {
344
+ {"t", 1, Qnil},
345
+ {NULL, 0, Qnil},
346
+ };
347
+ if (0 == table_id) {
348
+ table_id = rb_intern("table");
349
+ }
350
+ attrs[0].value = rb_funcall(obj, table_id, 0);
351
+
352
+ oj_code_attrs(obj, attrs, depth, out, true);
353
+ }
354
+
355
+ static void range_alt(VALUE obj, int depth, Out out) {
356
+ int d3 = depth + 2;
357
+ size_t size = d3 * out->indent + 2;
358
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
359
+ VALUE args[] = {Qundef, Qundef, Qundef, Qundef};
360
+
361
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
362
+
363
+ assure_size(out, size + sep_len + 6);
364
+ *out->cur++ = ',';
365
+ fill_indent(out, d3);
366
+ APPEND_CHARS(out->cur, "\"a\"", 3);
367
+ if (0 < out->opts->dump_opts.before_size) {
368
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
369
+ }
370
+ *out->cur++ = ':';
371
+ if (0 < out->opts->dump_opts.after_size) {
372
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
373
+ }
374
+ args[0] = rb_funcall(obj, oj_begin_id, 0);
375
+ args[1] = rb_funcall(obj, oj_end_id, 0);
376
+ args[2] = rb_funcall(obj, oj_exclude_end_id, 0);
377
+ dump_values_array(args, depth, out);
378
+ fill_indent(out, depth);
379
+ *out->cur++ = '}';
380
+ *out->cur = '\0';
381
+ }
382
+
383
+ static ID numerator_id = 0;
384
+ static ID denominator_id = 0;
385
+
386
+ static void rational_alt(VALUE obj, int depth, Out out) {
387
+ struct _attr attrs[] = {
388
+ {"n", 1, Qnil},
389
+ {"d", 1, Qnil},
390
+ {NULL, 0, Qnil},
391
+ };
392
+ if (0 == numerator_id) {
393
+ numerator_id = rb_intern("numerator");
394
+ denominator_id = rb_intern("denominator");
395
+ }
396
+ attrs[0].value = rb_funcall(obj, numerator_id, 0);
397
+ attrs[1].value = rb_funcall(obj, denominator_id, 0);
398
+
399
+ oj_code_attrs(obj, attrs, depth, out, true);
400
+ }
401
+
402
+ static ID options_id = 0;
403
+ static ID source_id = 0;
404
+
405
+ static void regexp_alt(VALUE obj, int depth, Out out) {
406
+ struct _attr attrs[] = {
407
+ {"o", 1, Qnil},
408
+ {"s", 1, Qnil},
409
+ {NULL, 0, Qnil},
410
+ };
411
+ if (0 == options_id) {
412
+ options_id = rb_intern("options");
413
+ source_id = rb_intern("source");
414
+ }
415
+ attrs[0].value = rb_funcall(obj, options_id, 0);
416
+ attrs[1].value = rb_funcall(obj, source_id, 0);
417
+
418
+ oj_code_attrs(obj, attrs, depth, out, true);
419
+ }
420
+
421
+ static void time_alt(VALUE obj, int depth, Out out) {
422
+ struct _attr attrs[] = {
423
+ {"s", 1, Qundef, 0, Qundef},
424
+ {"n", 1, Qundef, 0, Qundef},
425
+ {NULL, 0, Qnil},
426
+ };
427
+ time_t sec;
428
+ long long nsec;
429
+
430
+ if (16 <= sizeof(struct timespec)) {
431
+ struct timespec ts = rb_time_timespec(obj);
432
+
433
+ sec = (long long)ts.tv_sec;
434
+ nsec = ts.tv_nsec;
435
+ } else {
436
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
437
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
438
+ }
439
+
440
+ attrs[0].num = sec;
441
+ attrs[1].num = nsec;
442
+
443
+ oj_code_attrs(obj, attrs, depth, out, true);
444
+ }
445
+
446
+ struct _code oj_compat_codes[] = {
447
+ {"BigDecimal", Qnil, bigdecimal_alt, NULL, false},
448
+ {"Complex", Qnil, complex_alt, NULL, false},
449
+ {"Date", Qnil, date_alt, false},
450
+ {"DateTime", Qnil, datetime_alt, NULL, false},
451
+ {"OpenStruct", Qnil, openstruct_alt, NULL, false},
452
+ {"Range", Qnil, range_alt, NULL, false},
453
+ {"Rational", Qnil, rational_alt, NULL, false},
454
+ {"Regexp", Qnil, regexp_alt, NULL, false},
455
+ {"Time", Qnil, time_alt, NULL, false},
456
+ // TBD the rest of the library classes
457
+ {NULL, Qundef, NULL, NULL, false},
458
+ };
459
+
460
+ VALUE
461
+ oj_add_to_json(int argc, VALUE *argv, VALUE self) {
462
+ Code a;
463
+
464
+ if (0 == argc) {
465
+ for (a = oj_compat_codes; NULL != a->name; a++) {
466
+ if (Qnil == a->clas || Qundef == a->clas) {
467
+ a->clas = rb_const_get_at(rb_cObject, rb_intern(a->name));
468
+ }
469
+ a->active = true;
470
+ }
471
+ use_struct_alt = true;
472
+ use_exception_alt = true;
473
+ use_bignum_alt = true;
474
+ oj_use_hash_alt = true;
475
+ oj_use_array_alt = true;
476
+ } else {
477
+ for (; 0 < argc; argc--, argv++) {
478
+ if (rb_cStruct == *argv) {
479
+ use_struct_alt = true;
480
+ continue;
481
+ }
482
+ if (rb_eException == *argv) {
483
+ use_exception_alt = true;
484
+ continue;
485
+ }
486
+ if (rb_cInteger == *argv) {
487
+ use_bignum_alt = true;
488
+ continue;
489
+ }
490
+ if (rb_cHash == *argv) {
491
+ oj_use_hash_alt = true;
492
+ continue;
493
+ }
494
+ if (rb_cArray == *argv) {
495
+ oj_use_array_alt = true;
496
+ continue;
497
+ }
498
+ for (a = oj_compat_codes; NULL != a->name; a++) {
499
+ if (Qnil == a->clas || Qundef == a->clas) {
500
+ a->clas = rb_const_get_at(rb_cObject, rb_intern(a->name));
501
+ }
502
+ if (*argv == a->clas) {
503
+ a->active = true;
504
+ break;
505
+ }
506
+ }
507
+ }
508
+ }
509
+ return Qnil;
510
+ }
511
+
512
+ VALUE
513
+ oj_remove_to_json(int argc, VALUE *argv, VALUE self) {
514
+ if (0 == argc) {
515
+ oj_code_set_active(oj_compat_codes, Qnil, false);
516
+ use_struct_alt = false;
517
+ use_exception_alt = false;
518
+ use_bignum_alt = false;
519
+ oj_use_hash_alt = false;
520
+ oj_use_array_alt = false;
521
+ } else {
522
+ for (; 0 < argc; argc--, argv++) {
523
+ if (rb_cStruct == *argv) {
524
+ use_struct_alt = false;
525
+ continue;
526
+ }
527
+ if (rb_eException == *argv) {
528
+ use_exception_alt = false;
529
+ continue;
530
+ }
531
+ if (rb_cInteger == *argv) {
532
+ use_bignum_alt = false;
533
+ continue;
534
+ }
535
+ if (rb_cHash == *argv) {
536
+ oj_use_hash_alt = false;
537
+ continue;
538
+ }
539
+ if (rb_cArray == *argv) {
540
+ oj_use_array_alt = false;
541
+ continue;
542
+ }
543
+ oj_code_set_active(oj_compat_codes, *argv, false);
544
+ }
545
+ }
546
+ return Qnil;
547
+ }
548
+
549
+ // The JSON gem is inconsistent with handling of infinity. Using
550
+ // JSON.dump(0.1/0) returns the string Infinity but (0.1/0).to_json raise and
551
+ // exception. Worse, for BigDecimals a quoted "Infinity" is returned.
552
+ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
553
+ char buf[64];
554
+ char *b;
555
+ double d = rb_num2dbl(obj);
556
+ size_t cnt = 0;
557
+
558
+ if (0.0 == d) {
559
+ b = buf;
560
+ *b++ = '0';
561
+ *b++ = '.';
562
+ *b++ = '0';
563
+ *b++ = '\0';
564
+ cnt = 3;
565
+ } else if (OJ_INFINITY == d) {
566
+ if (WordNan == out->opts->dump_opts.nan_dump) {
567
+ strcpy(buf, "Infinity");
568
+ cnt = 8;
569
+ } else {
570
+ raise_json_err("Infinity not allowed in JSON.", "GeneratorError");
571
+ }
572
+ } else if (-OJ_INFINITY == d) {
573
+ if (WordNan == out->opts->dump_opts.nan_dump) {
574
+ strcpy(buf, "-Infinity");
575
+ cnt = 9;
576
+ } else {
577
+ raise_json_err("-Infinity not allowed in JSON.", "GeneratorError");
578
+ }
579
+ } else if (isnan(d)) {
580
+ if (WordNan == out->opts->dump_opts.nan_dump) {
581
+ strcpy(buf, "NaN");
582
+ cnt = 3;
583
+ } else {
584
+ raise_json_err("NaN not allowed in JSON.", "GeneratorError");
585
+ }
586
+ } else if (d == (double)(long long int)d) {
587
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
588
+ } else if (oj_rails_float_opt) {
589
+ cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, "%0.16g");
590
+ } else {
591
+ volatile VALUE rstr = oj_safe_string_convert(obj);
592
+
593
+ strcpy(buf, RSTRING_PTR(rstr));
594
+ cnt = RSTRING_LEN(rstr);
595
+ }
596
+ assure_size(out, cnt);
597
+ APPEND_CHARS(out->cur, buf, cnt);
598
+ *out->cur = '\0';
599
+ }
600
+
601
+ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
602
+ Out out = (Out)ov;
603
+ int depth = out->depth;
604
+
605
+ if (out->omit_nil && Qnil == value) {
606
+ return ST_CONTINUE;
607
+ }
608
+ if (!out->opts->dump_opts.use) {
609
+ assure_size(out, depth * out->indent + 1);
610
+ fill_indent(out, depth);
611
+ } else {
612
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
613
+ if (0 < out->opts->dump_opts.hash_size) {
614
+ APPEND_CHARS(out->cur, out->opts->dump_opts.hash_nl, out->opts->dump_opts.hash_size);
615
+ }
616
+ if (0 < out->opts->dump_opts.indent_size) {
617
+ int i;
618
+ for (i = depth; 0 < i; i--) {
619
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
620
+ }
621
+ }
622
+ }
623
+ switch (rb_type(key)) {
624
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
625
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
626
+ default:
627
+ /*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n",
628
+ * rb_class2name(rb_obj_class(key)));*/
629
+ oj_dump_str(oj_safe_string_convert(key), 0, out, false);
630
+ break;
631
+ }
632
+ if (!out->opts->dump_opts.use) {
633
+ *out->cur++ = ':';
634
+ } else {
635
+ assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
636
+ if (0 < out->opts->dump_opts.before_size) {
637
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
638
+ }
639
+ *out->cur++ = ':';
640
+ if (0 < out->opts->dump_opts.after_size) {
641
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
642
+ }
643
+ }
644
+ oj_dump_compat_val(value, depth, out, true);
645
+ out->depth = depth;
646
+ *out->cur++ = ',';
647
+
648
+ return ST_CONTINUE;
649
+ }
650
+
651
+ static void dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
652
+ int cnt;
653
+ long id = oj_check_circular(obj, out);
654
+
655
+ if (0 > id) {
656
+ raise_json_err("Too deeply nested", "NestingError");
657
+ return;
658
+ }
659
+ if (as_ok && !oj_use_hash_alt && rb_obj_class(obj) != rb_cHash && rb_respond_to(obj, oj_to_json_id)) {
660
+ dump_to_json(obj, out);
661
+ return;
662
+ }
663
+ cnt = (int)RHASH_SIZE(obj);
664
+ assure_size(out, 2);
665
+ if (0 == cnt) {
666
+ APPEND_CHARS(out->cur, "{}", 2);
667
+ } else {
668
+ *out->cur++ = '{';
669
+ out->depth = depth + 1;
670
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
671
+ if (',' == *(out->cur - 1)) {
672
+ out->cur--; // backup to overwrite last comma
673
+ }
674
+ if (!out->opts->dump_opts.use) {
675
+ assure_size(out, depth * out->indent + 2);
676
+ fill_indent(out, depth);
677
+ } else {
678
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
679
+ if (0 < out->opts->dump_opts.hash_size) {
680
+ APPEND_CHARS(out->cur, out->opts->dump_opts.hash_nl, out->opts->dump_opts.hash_size);
681
+ }
682
+ if (0 < out->opts->dump_opts.indent_size) {
683
+ int i;
684
+
685
+ for (i = depth; 0 < i; i--) {
686
+ APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
687
+ }
688
+ }
689
+ }
690
+ *out->cur++ = '}';
691
+ }
692
+ *out->cur = '\0';
693
+ }
694
+
695
+ // In compat mode only the first call check for to_json. After that to_s is
696
+ // called.
697
+ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
698
+ if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
699
+ return;
700
+ }
701
+ if (use_exception_alt && rb_obj_is_kind_of(obj, rb_eException)) {
702
+ exception_alt(obj, depth, out);
703
+ return;
704
+ }
705
+ if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
706
+ oj_dump_raw_json(obj, depth, out);
707
+ return;
708
+ }
709
+ if (as_ok && rb_respond_to(obj, oj_to_json_id)) {
710
+ dump_to_json(obj, out);
711
+ return;
712
+ }
713
+ // Nothing else matched so encode as a JSON object with Ruby obj members
714
+ // as JSON object members.
715
+ oj_dump_obj_to_s(obj, out);
716
+ }
717
+
718
+ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
719
+ VALUE clas = rb_obj_class(obj);
720
+
721
+ if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
722
+ return;
723
+ }
724
+ if (rb_cRange == clas) {
725
+ *out->cur++ = '"';
726
+ oj_dump_compat_val(rb_funcall(obj, oj_begin_id, 0), 0, out, false);
727
+ assure_size(out, 3);
728
+ APPEND_CHARS(out->cur, "..", 2);
729
+ if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
730
+ *out->cur++ = '.';
731
+ }
732
+ oj_dump_compat_val(rb_funcall(obj, oj_end_id, 0), 0, out, false);
733
+ *out->cur++ = '"';
734
+
735
+ return;
736
+ }
737
+ if (as_ok && rb_respond_to(obj, oj_to_json_id)) {
738
+ dump_to_json(obj, out);
739
+
740
+ return;
741
+ }
742
+ if (use_struct_alt) {
743
+ int d3 = depth + 2;
744
+ size_t size = d3 * out->indent + 2;
745
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
746
+ const char *classname = rb_class2name(rb_obj_class(obj));
747
+ VALUE args[100];
748
+ int cnt;
749
+ int i;
750
+
751
+ if (NULL == classname || '#' == *classname) {
752
+ raise_json_err("Only named structs are supported.", "JSONError");
753
+ }
754
+ #ifdef RSTRUCT_LEN
755
+ #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
756
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
757
+ #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
758
+ cnt = (int)RSTRUCT_LEN(obj);
759
+ #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
760
+ #else
761
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
762
+ // class in interpreted Ruby so length() may not be defined.
763
+ cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
764
+ #endif
765
+ if (sizeof(args) / sizeof(*args) <= (size_t)cnt) {
766
+ // TBD allocate and try again
767
+ cnt = 99;
768
+ }
769
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
770
+
771
+ assure_size(out, size + sep_len + 6);
772
+ *out->cur++ = ',';
773
+ fill_indent(out, d3);
774
+ APPEND_CHARS(out->cur, "\"v\"", 3);
775
+ if (0 < out->opts->dump_opts.before_size) {
776
+ APPEND_CHARS(out->cur, out->opts->dump_opts.before_sep, out->opts->dump_opts.before_size);
777
+ }
778
+ *out->cur++ = ':';
779
+ if (0 < out->opts->dump_opts.after_size) {
780
+ APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
781
+ }
782
+ for (i = 0; i < cnt; i++) {
783
+ #ifdef RSTRUCT_LEN
784
+ args[i] = RSTRUCT_GET(obj, i);
785
+ #else
786
+ args[i] = rb_struct_aref(obj, INT2FIX(i));
787
+ #endif
788
+ }
789
+ args[cnt] = Qundef;
790
+ dump_values_array(args, depth, out);
791
+ fill_indent(out, depth);
792
+ *out->cur++ = '}';
793
+ *out->cur = '\0';
794
+ } else {
795
+ oj_dump_obj_to_s(obj, out);
796
+ }
797
+ }
798
+
799
+ static void dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
800
+ // The json gem uses to_s explicitly. to_s can be overridden while
801
+ // rb_big2str can not so unless overridden by using add_to_json(Integer)
802
+ // this must use to_s to pass the json gem unit tests.
803
+ volatile VALUE rs;
804
+ size_t cnt;
805
+ bool dump_as_string = false;
806
+
807
+ if (use_bignum_alt) {
808
+ rs = rb_big2str(obj, 10);
809
+ } else {
810
+ rs = oj_safe_string_convert(obj);
811
+ }
812
+ rb_check_type(rs, T_STRING);
813
+ cnt = RSTRING_LEN(rs);
814
+
815
+ if (out->opts->int_range_min != 0 || out->opts->int_range_max != 0) {
816
+ dump_as_string = true; // Bignum cannot be inside of Fixnum range
817
+ assure_size(out, cnt + 2);
818
+ *out->cur++ = '"';
819
+ } else {
820
+ assure_size(out, cnt);
821
+ }
822
+ APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
823
+ if (dump_as_string) {
824
+ *out->cur++ = '"';
825
+ }
826
+ *out->cur = '\0';
827
+ }
828
+
829
+ static DumpFunc compat_funcs[] = {
830
+ NULL, // RUBY_T_NONE = 0x00,
831
+ dump_obj, // RUBY_T_OBJECT = 0x01,
832
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
833
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
834
+ dump_float, // RUBY_T_FLOAT = 0x04,
835
+ oj_dump_str, // RUBY_T_STRING = 0x05,
836
+ dump_obj, // RUBY_T_REGEXP = 0x06,
837
+ dump_array, // RUBY_T_ARRAY = 0x07,
838
+ dump_hash, // RUBY_T_HASH = 0x08,
839
+ dump_struct, // RUBY_T_STRUCT = 0x09,
840
+ dump_bignum, // RUBY_T_BIGNUM = 0x0a,
841
+ NULL, // RUBY_T_FILE = 0x0b,
842
+ dump_obj, // RUBY_T_DATA = 0x0c,
843
+ NULL, // RUBY_T_MATCH = 0x0d,
844
+ dump_obj, // RUBY_T_COMPLEX = 0x0e,
845
+ dump_obj, // RUBY_T_RATIONAL = 0x0f,
846
+ NULL, // 0x10
847
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
848
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
849
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
850
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
851
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
852
+ };
853
+
854
+ static void set_state_depth(VALUE state, int depth) {
855
+ if (0 == rb_const_defined(rb_cObject, rb_intern("JSON"))) {
856
+ rb_require("oj/json");
857
+ }
858
+ {
859
+ VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
860
+ VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
861
+ VALUE generator = rb_const_get(ext, rb_intern("Generator"));
862
+ VALUE state_class = rb_const_get(generator, rb_intern("State"));
863
+
864
+ if (state_class == rb_obj_class(state)) {
865
+ rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
866
+ }
867
+ }
868
+ }
869
+
870
+ void oj_dump_compat_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
+ // The max_nesting logic is that an empty Array or Hash is assumed to have
875
+ // content so the max_nesting should fail but a non-collection value is
876
+ // okay. That means a check for a collectable value is needed before
877
+ // raising.
878
+ if (out->opts->dump_opts.max_depth <= depth) {
879
+ if (RUBY_T_ARRAY == type || RUBY_T_HASH == type) {
880
+ if (0 < out->argc) {
881
+ set_state_depth(*out->argv, depth);
882
+ }
883
+ raise_json_err("Too deeply nested", "NestingError");
884
+ }
885
+ }
886
+ if (0 < type && type <= RUBY_T_FIXNUM) {
887
+ DumpFunc f = compat_funcs[type];
888
+
889
+ if (NULL != f) {
890
+ f(obj, depth, out, as_ok);
891
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
892
+ return;
893
+ }
894
+ }
895
+ oj_dump_nil(Qnil, depth, out, false);
896
+ TRACE(out->opts->trace, "dump", Qnil, depth, TraceOut);
897
+ }