oj 3.10.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +104 -0
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +107 -0
  6. data/ext/oj/cache8.h +48 -0
  7. data/ext/oj/circarray.c +68 -0
  8. data/ext/oj/circarray.h +23 -0
  9. data/ext/oj/code.c +235 -0
  10. data/ext/oj/code.h +42 -0
  11. data/ext/oj/compat.c +299 -0
  12. data/ext/oj/custom.c +1218 -0
  13. data/ext/oj/dump.c +1249 -0
  14. data/ext/oj/dump.h +96 -0
  15. data/ext/oj/dump_compat.c +975 -0
  16. data/ext/oj/dump_leaf.c +252 -0
  17. data/ext/oj/dump_object.c +844 -0
  18. data/ext/oj/dump_strict.c +434 -0
  19. data/ext/oj/encode.h +45 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +53 -0
  23. data/ext/oj/fast.c +1771 -0
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +890 -0
  28. data/ext/oj/object.c +775 -0
  29. data/ext/oj/odd.c +231 -0
  30. data/ext/oj/odd.h +44 -0
  31. data/ext/oj/oj.c +1723 -0
  32. data/ext/oj/oj.h +387 -0
  33. data/ext/oj/parse.c +1134 -0
  34. data/ext/oj/parse.h +112 -0
  35. data/ext/oj/rails.c +1528 -0
  36. data/ext/oj/rails.h +21 -0
  37. data/ext/oj/reader.c +231 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +102 -0
  40. data/ext/oj/resolve.h +14 -0
  41. data/ext/oj/rxclass.c +147 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +714 -0
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +924 -0
  46. data/ext/oj/stream_writer.c +363 -0
  47. data/ext/oj/strict.c +212 -0
  48. data/ext/oj/string_writer.c +534 -0
  49. data/ext/oj/trace.c +79 -0
  50. data/ext/oj/trace.h +28 -0
  51. data/ext/oj/util.c +136 -0
  52. data/ext/oj/util.h +19 -0
  53. data/ext/oj/val_stack.c +118 -0
  54. data/ext/oj/val_stack.h +185 -0
  55. data/ext/oj/wab.c +631 -0
  56. data/lib/oj.rb +21 -0
  57. data/lib/oj/active_support_helper.rb +41 -0
  58. data/lib/oj/bag.rb +88 -0
  59. data/lib/oj/easy_hash.rb +52 -0
  60. data/lib/oj/error.rb +22 -0
  61. data/lib/oj/json.rb +176 -0
  62. data/lib/oj/mimic.rb +267 -0
  63. data/lib/oj/saj.rb +66 -0
  64. data/lib/oj/schandler.rb +142 -0
  65. data/lib/oj/state.rb +131 -0
  66. data/lib/oj/version.rb +5 -0
  67. data/pages/Advanced.md +22 -0
  68. data/pages/Compatibility.md +25 -0
  69. data/pages/Custom.md +23 -0
  70. data/pages/Encoding.md +65 -0
  71. data/pages/JsonGem.md +79 -0
  72. data/pages/Modes.md +155 -0
  73. data/pages/Options.md +287 -0
  74. data/pages/Rails.md +155 -0
  75. data/pages/Security.md +20 -0
  76. data/pages/WAB.md +13 -0
  77. data/test/_test_active.rb +76 -0
  78. data/test/_test_active_mimic.rb +96 -0
  79. data/test/_test_mimic_rails.rb +126 -0
  80. data/test/activerecord/result_test.rb +27 -0
  81. data/test/activesupport4/decoding_test.rb +108 -0
  82. data/test/activesupport4/encoding_test.rb +531 -0
  83. data/test/activesupport4/test_helper.rb +41 -0
  84. data/test/activesupport5/abstract_unit.rb +45 -0
  85. data/test/activesupport5/decoding_test.rb +133 -0
  86. data/test/activesupport5/encoding_test.rb +500 -0
  87. data/test/activesupport5/encoding_test_cases.rb +98 -0
  88. data/test/activesupport5/test_helper.rb +72 -0
  89. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  90. data/test/activesupport6/abstract_unit.rb +44 -0
  91. data/test/activesupport6/decoding_test.rb +133 -0
  92. data/test/activesupport6/encoding_test.rb +507 -0
  93. data/test/activesupport6/encoding_test_cases.rb +98 -0
  94. data/test/activesupport6/test_common.rb +17 -0
  95. data/test/activesupport6/test_helper.rb +163 -0
  96. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  97. data/test/bar.rb +35 -0
  98. data/test/baz.rb +16 -0
  99. data/test/files.rb +29 -0
  100. data/test/foo.rb +52 -0
  101. data/test/helper.rb +26 -0
  102. data/test/isolated/shared.rb +308 -0
  103. data/test/isolated/test_mimic_after.rb +13 -0
  104. data/test/isolated/test_mimic_alone.rb +12 -0
  105. data/test/isolated/test_mimic_as_json.rb +45 -0
  106. data/test/isolated/test_mimic_before.rb +13 -0
  107. data/test/isolated/test_mimic_define.rb +28 -0
  108. data/test/isolated/test_mimic_rails_after.rb +22 -0
  109. data/test/isolated/test_mimic_rails_before.rb +21 -0
  110. data/test/isolated/test_mimic_redefine.rb +15 -0
  111. data/test/json_gem/json_addition_test.rb +216 -0
  112. data/test/json_gem/json_common_interface_test.rb +148 -0
  113. data/test/json_gem/json_encoding_test.rb +107 -0
  114. data/test/json_gem/json_ext_parser_test.rb +20 -0
  115. data/test/json_gem/json_fixtures_test.rb +35 -0
  116. data/test/json_gem/json_generator_test.rb +383 -0
  117. data/test/json_gem/json_generic_object_test.rb +90 -0
  118. data/test/json_gem/json_parser_test.rb +470 -0
  119. data/test/json_gem/json_string_matching_test.rb +42 -0
  120. data/test/json_gem/test_helper.rb +18 -0
  121. data/test/perf.rb +107 -0
  122. data/test/perf_compat.rb +130 -0
  123. data/test/perf_fast.rb +164 -0
  124. data/test/perf_file.rb +64 -0
  125. data/test/perf_object.rb +138 -0
  126. data/test/perf_saj.rb +109 -0
  127. data/test/perf_scp.rb +151 -0
  128. data/test/perf_simple.rb +287 -0
  129. data/test/perf_strict.rb +145 -0
  130. data/test/perf_wab.rb +131 -0
  131. data/test/prec.rb +23 -0
  132. data/test/sample.rb +54 -0
  133. data/test/sample/change.rb +14 -0
  134. data/test/sample/dir.rb +19 -0
  135. data/test/sample/doc.rb +36 -0
  136. data/test/sample/file.rb +48 -0
  137. data/test/sample/group.rb +16 -0
  138. data/test/sample/hasprops.rb +16 -0
  139. data/test/sample/layer.rb +12 -0
  140. data/test/sample/line.rb +20 -0
  141. data/test/sample/oval.rb +10 -0
  142. data/test/sample/rect.rb +10 -0
  143. data/test/sample/shape.rb +35 -0
  144. data/test/sample/text.rb +20 -0
  145. data/test/sample_json.rb +37 -0
  146. data/test/test_compat.rb +502 -0
  147. data/test/test_custom.rb +527 -0
  148. data/test/test_debian.rb +53 -0
  149. data/test/test_fast.rb +470 -0
  150. data/test/test_file.rb +239 -0
  151. data/test/test_gc.rb +49 -0
  152. data/test/test_hash.rb +29 -0
  153. data/test/test_integer_range.rb +72 -0
  154. data/test/test_null.rb +376 -0
  155. data/test/test_object.rb +1027 -0
  156. data/test/test_rails.rb +26 -0
  157. data/test/test_saj.rb +186 -0
  158. data/test/test_scp.rb +433 -0
  159. data/test/test_strict.rb +433 -0
  160. data/test/test_various.rb +719 -0
  161. data/test/test_wab.rb +307 -0
  162. data/test/test_writer.rb +380 -0
  163. data/test/tests.rb +25 -0
  164. data/test/tests_mimic.rb +14 -0
  165. data/test/tests_mimic_addition.rb +7 -0
  166. data/test/zoo.rb +13 -0
  167. metadata +381 -0
@@ -0,0 +1,96 @@
1
+ /* dump.h
2
+ * Copyright (c) 2011, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #ifndef OJ_DUMP_H
7
+ #define OJ_DUMP_H
8
+
9
+ #include <ruby.h>
10
+
11
+ #include "oj.h"
12
+
13
+ #define MAX_DEPTH 1000
14
+
15
+ // Extra padding at end of buffer.
16
+ #define BUFFER_EXTRA 64
17
+
18
+ extern void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok);
19
+ extern void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok);
20
+ extern void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok);
21
+ extern void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok);
22
+ extern void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok);
23
+ extern void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok);
24
+ extern void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok);
25
+ extern void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok);
26
+ extern void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok);
27
+
28
+ extern void oj_dump_raw(const char *str, size_t cnt, Out out);
29
+ extern void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out);
30
+ extern void oj_dump_ruby_time(VALUE obj, Out out);
31
+ extern void oj_dump_xml_time(VALUE obj, Out out);
32
+ extern void oj_dump_time(VALUE obj, Out out, int withZone);
33
+ extern void oj_dump_obj_to_s(VALUE obj, Out out);
34
+
35
+ extern const char* oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp);
36
+
37
+ extern void oj_grow_out(Out out, size_t len);
38
+ extern long oj_check_circular(VALUE obj, Out out);
39
+
40
+ extern void oj_dump_strict_val(VALUE obj, int depth, Out out);
41
+ extern void oj_dump_null_val(VALUE obj, int depth, Out out);
42
+ extern void oj_dump_obj_val(VALUE obj, int depth, Out out);
43
+ extern void oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok);
44
+ extern void oj_dump_rails_val(VALUE obj, int depth, Out out);
45
+ extern void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok);
46
+ extern void oj_dump_wab_val(VALUE obj, int depth, Out out);
47
+
48
+ extern void oj_dump_raw_json(VALUE obj, int depth, Out out);
49
+
50
+ extern VALUE oj_add_to_json(int argc, VALUE *argv, VALUE self);
51
+ extern VALUE oj_remove_to_json(int argc, VALUE *argv, VALUE self);
52
+
53
+ extern int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format);
54
+
55
+ extern bool oj_dump_ignore(Options opts, VALUE obj);
56
+ extern time_t oj_sec_from_time_hard_way(VALUE obj);
57
+
58
+ inline static void
59
+ assure_size(Out out, size_t len) {
60
+ if (out->end - out->cur <= (long)len) {
61
+ oj_grow_out(out, len);
62
+ }
63
+ }
64
+
65
+ inline static void
66
+ fill_indent(Out out, int cnt) {
67
+ if (0 < out->indent) {
68
+ cnt *= out->indent;
69
+ *out->cur++ = '\n';
70
+ for (; 0 < cnt; cnt--) {
71
+ *out->cur++ = ' ';
72
+ }
73
+ }
74
+ }
75
+
76
+ inline static void
77
+ dump_ulong(unsigned long num, Out out) {
78
+ char buf[32];
79
+ char *b = buf + sizeof(buf) - 1;
80
+
81
+ *b-- = '\0';
82
+ if (0 < num) {
83
+ for (; 0 < num; num /= 10, b--) {
84
+ *b = (num % 10) + '0';
85
+ }
86
+ b++;
87
+ } else {
88
+ *b = '0';
89
+ }
90
+ for (; '\0' != *b; b++) {
91
+ *out->cur++ = *b;
92
+ }
93
+ *out->cur = '\0';
94
+ }
95
+
96
+ #endif /* OJ_DUMP_H */
@@ -0,0 +1,975 @@
1
+ /* dump_object.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include "code.h"
7
+ #include "dump.h"
8
+ #include "rails.h"
9
+ #include "trace.h"
10
+
11
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
12
+ #define OJ_INFINITY (1.0/0.0)
13
+
14
+ bool oj_use_hash_alt = false;
15
+ bool oj_use_array_alt = false;
16
+
17
+ static bool use_struct_alt = false;
18
+ static bool use_exception_alt = false;
19
+ static bool use_bignum_alt = false;
20
+
21
+ static void
22
+ raise_json_err(const char *msg, const char *err_classname) {
23
+ rb_raise(oj_get_json_err_class(err_classname), "%s", msg);
24
+ }
25
+
26
+ static void
27
+ dump_obj_classname(const char *classname, int depth, Out out) {
28
+ int d2 = depth + 1;
29
+ size_t len = strlen(classname);
30
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
31
+ size_t size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
32
+
33
+ assure_size(out, size);
34
+ *out->cur++ = '{';
35
+ fill_indent(out, d2);
36
+ *out->cur++ = '"';
37
+ memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
38
+ out->cur += out->opts->create_id_len;
39
+ *out->cur++ = '"';
40
+ if (0 < out->opts->dump_opts.before_size) {
41
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
42
+ out->cur += out->opts->dump_opts.before_size;
43
+ }
44
+ *out->cur++ = ':';
45
+ if (0 < out->opts->dump_opts.after_size) {
46
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
47
+ out->cur += out->opts->dump_opts.after_size;
48
+ }
49
+ *out->cur++ = '"';
50
+ memcpy(out->cur, classname, len);
51
+ out->cur += len;
52
+ *out->cur++ = '"';
53
+ }
54
+
55
+ static void
56
+ dump_values_array(VALUE *values, int depth, Out out) {
57
+ size_t size;
58
+ int d2 = depth + 1;
59
+
60
+ assure_size(out, d2 * out->indent + 3);
61
+ *out->cur++ = '[';
62
+ if (Qundef == *values) {
63
+ *out->cur++ = ']';
64
+ } else {
65
+ if (out->opts->dump_opts.use) {
66
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 2;
67
+ size += out->opts->dump_opts.array_size;
68
+ size += out->opts->dump_opts.indent_size;
69
+ } else {
70
+ size = d2 * out->indent + 3;
71
+ }
72
+ for (; Qundef != *values; values++) {
73
+ assure_size(out, size);
74
+ if (out->opts->dump_opts.use) {
75
+ if (0 < out->opts->dump_opts.array_size) {
76
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
77
+ out->cur += out->opts->dump_opts.array_size;
78
+ }
79
+ if (0 < out->opts->dump_opts.indent_size) {
80
+ int i;
81
+
82
+ for (i = d2; 0 < i; i--) {
83
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
84
+ out->cur += out->opts->dump_opts.indent_size;
85
+ }
86
+ }
87
+ } else {
88
+ fill_indent(out, d2);
89
+ }
90
+ oj_dump_compat_val(*values, d2, out, true);
91
+ if (Qundef != *(values + 1)) {
92
+ *out->cur++ = ',';
93
+ }
94
+ }
95
+ assure_size(out, size);
96
+ if (out->opts->dump_opts.use) {
97
+ if (0 < out->opts->dump_opts.array_size) {
98
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
99
+ out->cur += out->opts->dump_opts.array_size;
100
+ }
101
+ if (0 < out->opts->dump_opts.indent_size) {
102
+ int i;
103
+
104
+ for (i = depth; 0 < i; i--) {
105
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
106
+ out->cur += out->opts->dump_opts.indent_size;
107
+ }
108
+ }
109
+ } else {
110
+ fill_indent(out, depth);
111
+ }
112
+ *out->cur++ = ']';
113
+ }
114
+ }
115
+
116
+ static void
117
+ dump_to_json(VALUE obj, Out out) {
118
+ volatile VALUE rs;
119
+ const char *s;
120
+ int len;
121
+
122
+ if (Yes == out->opts->trace) {
123
+ oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyIn);
124
+ }
125
+ if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
126
+ rs = rb_funcall(obj, oj_to_json_id, 0);
127
+ } else {
128
+ rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
129
+ }
130
+ if (Yes == out->opts->trace) {
131
+ oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyOut);
132
+ }
133
+
134
+ s = rb_string_value_ptr((VALUE*)&rs);
135
+ len = (int)RSTRING_LEN(rs);
136
+
137
+ assure_size(out, len + 1);
138
+ memcpy(out->cur, s, len);
139
+ out->cur += len;
140
+ *out->cur = '\0';
141
+ }
142
+
143
+ static void
144
+ dump_array(VALUE a, int depth, Out out, bool as_ok) {
145
+ size_t size;
146
+ int i, cnt;
147
+ int d2 = depth + 1;
148
+ long id = oj_check_circular(a, out);
149
+
150
+ if (0 > id) {
151
+ raise_json_err("Too deeply nested", "NestingError");
152
+ return;
153
+ }
154
+ if (as_ok && !oj_use_hash_alt && rb_obj_class(a) != rb_cArray && rb_respond_to(a, oj_to_json_id)) {
155
+ dump_to_json(a, out);
156
+ return;
157
+ }
158
+ cnt = (int)RARRAY_LEN(a);
159
+ *out->cur++ = '[';
160
+ assure_size(out, 2);
161
+ if (0 == cnt) {
162
+ *out->cur++ = ']';
163
+ } else {
164
+ if (out->opts->dump_opts.use) {
165
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
166
+ } else {
167
+ size = d2 * out->indent + 2;
168
+ }
169
+ cnt--;
170
+ for (i = 0; i <= cnt; i++) {
171
+ assure_size(out, size);
172
+ if (out->opts->dump_opts.use) {
173
+ if (0 < out->opts->dump_opts.array_size) {
174
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
175
+ out->cur += out->opts->dump_opts.array_size;
176
+ }
177
+ if (0 < out->opts->dump_opts.indent_size) {
178
+ int i;
179
+ for (i = d2; 0 < i; i--) {
180
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
181
+ out->cur += out->opts->dump_opts.indent_size;
182
+ }
183
+ }
184
+ } else {
185
+ fill_indent(out, d2);
186
+ }
187
+ oj_dump_compat_val(rb_ary_entry(a, i), d2, out, true);
188
+ if (i < cnt) {
189
+ *out->cur++ = ',';
190
+ }
191
+ }
192
+ if (out->opts->dump_opts.use) {
193
+ size = out->opts->dump_opts.array_size + out->opts->dump_opts.indent_size * depth + 1;
194
+ assure_size(out, size);
195
+ if (0 < out->opts->dump_opts.array_size) {
196
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
197
+ out->cur += out->opts->dump_opts.array_size;
198
+ }
199
+ if (0 < out->opts->dump_opts.indent_size) {
200
+ int i;
201
+
202
+ for (i = depth; 0 < i; i--) {
203
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
204
+ out->cur += out->opts->dump_opts.indent_size;
205
+ }
206
+ }
207
+ } else {
208
+ size = depth * out->indent + 1;
209
+ assure_size(out, size);
210
+ fill_indent(out, depth);
211
+ }
212
+ *out->cur++ = ']';
213
+ }
214
+ *out->cur = '\0';
215
+ }
216
+
217
+ static ID _dump_id = 0;
218
+
219
+ static void
220
+ bigdecimal_alt(VALUE obj, int depth, Out out) {
221
+ struct _attr attrs[] = {
222
+ { "b", 1, Qnil },
223
+ { NULL, 0, Qnil },
224
+ };
225
+
226
+ if (0 == _dump_id) {
227
+ _dump_id = rb_intern("_dump");
228
+ }
229
+ attrs[0].value = rb_funcall(obj, _dump_id, 0);
230
+
231
+ oj_code_attrs(obj, attrs, depth, out, true);
232
+ }
233
+
234
+ static ID real_id = 0;
235
+ static ID imag_id = 0;
236
+
237
+ static void
238
+ complex_alt(VALUE obj, int depth, Out out) {
239
+ struct _attr attrs[] = {
240
+ { "r", 1, Qnil },
241
+ { "i", 1, Qnil },
242
+ { NULL, 0, Qnil },
243
+ };
244
+
245
+ if (0 == real_id) {
246
+ real_id = rb_intern("real");
247
+ imag_id = rb_intern("imag");
248
+ }
249
+ attrs[0].value = rb_funcall(obj, real_id, 0);
250
+ attrs[1].value = rb_funcall(obj, imag_id, 0);
251
+
252
+ oj_code_attrs(obj, attrs, depth, out, true);
253
+ }
254
+
255
+ static ID year_id = 0;
256
+ static ID month_id = 0;
257
+ static ID day_id = 0;
258
+ static ID start_id = 0;
259
+
260
+ static void
261
+ date_alt(VALUE obj, int depth, Out out) {
262
+ struct _attr attrs[] = {
263
+ { "y", 1, Qnil },
264
+ { "m", 1, Qnil },
265
+ { "d", 1, Qnil },
266
+ { "sg", 2, Qnil },
267
+ { NULL, 0, Qnil },
268
+ };
269
+ if (0 == year_id) {
270
+ year_id = rb_intern("year");
271
+ month_id = rb_intern("month");
272
+ day_id = rb_intern("day");
273
+ start_id = rb_intern("start");
274
+ }
275
+ attrs[0].value = rb_funcall(obj, year_id, 0);
276
+ attrs[1].value = rb_funcall(obj, month_id, 0);
277
+ attrs[2].value = rb_funcall(obj, day_id, 0);
278
+ attrs[3].value = rb_funcall(obj, start_id, 0);
279
+
280
+ oj_code_attrs(obj, attrs, depth, out, true);
281
+ }
282
+
283
+ static ID hour_id = 0;
284
+ static ID min_id = 0;
285
+ static ID sec_id = 0;
286
+ static ID offset_id = 0;
287
+
288
+ static void
289
+ datetime_alt(VALUE obj, int depth, Out out) {
290
+ struct _attr attrs[] = {
291
+ { "y", 1, Qnil },
292
+ { "m", 1, Qnil },
293
+ { "d", 1, Qnil },
294
+ { "H", 1, Qnil },
295
+ { "M", 1, Qnil },
296
+ { "S", 1, Qnil },
297
+ { "of", 2, Qnil },
298
+ { "sg", 2, Qnil },
299
+ { NULL, 0, Qnil },
300
+ };
301
+ if (0 == hour_id) {
302
+ year_id = rb_intern("year");
303
+ month_id = rb_intern("month");
304
+ day_id = rb_intern("day");
305
+ hour_id = rb_intern("hour");
306
+ min_id = rb_intern("min");
307
+ sec_id = rb_intern("sec");
308
+ offset_id = rb_intern("offset");
309
+ start_id = rb_intern("start");
310
+ }
311
+ attrs[0].value = rb_funcall(obj, year_id, 0);
312
+ attrs[1].value = rb_funcall(obj, month_id, 0);
313
+ attrs[2].value = rb_funcall(obj, day_id, 0);
314
+ attrs[3].value = rb_funcall(obj, hour_id, 0);
315
+ attrs[4].value = rb_funcall(obj, min_id, 0);
316
+ attrs[5].value = rb_funcall(obj, sec_id, 0);
317
+ attrs[6].value = rb_funcall(rb_funcall(obj, offset_id, 0), oj_to_s_id, 0);
318
+ attrs[7].value = rb_funcall(obj, start_id, 0);
319
+
320
+ oj_code_attrs(obj, attrs, depth, out, true);
321
+ }
322
+
323
+ static ID message_id = 0;
324
+ static ID backtrace_id = 0;
325
+
326
+ static void
327
+ exception_alt(VALUE obj, int depth, Out out) {
328
+ int d3 = depth + 2;
329
+ size_t size = d3 * out->indent + 2;
330
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
331
+
332
+ if (0 == message_id) {
333
+ message_id = rb_intern("message");
334
+ backtrace_id = rb_intern("backtrace");
335
+ }
336
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
337
+
338
+ assure_size(out, size + sep_len + 6);
339
+ *out->cur++ = ',';
340
+ fill_indent(out, d3);
341
+ *out->cur++ = '"';
342
+ *out->cur++ = 'm';
343
+ *out->cur++ = '"';
344
+ if (0 < out->opts->dump_opts.before_size) {
345
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
346
+ out->cur += out->opts->dump_opts.before_size;
347
+ }
348
+ *out->cur++ = ':';
349
+ if (0 < out->opts->dump_opts.after_size) {
350
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
351
+ out->cur += out->opts->dump_opts.after_size;
352
+ }
353
+ oj_dump_str(rb_funcall(obj, message_id, 0), 0, out, false);
354
+ assure_size(out, size + sep_len + 6);
355
+ *out->cur++ = ',';
356
+ fill_indent(out, d3);
357
+ *out->cur++ = '"';
358
+ *out->cur++ = 'b';
359
+ *out->cur++ = '"';
360
+ if (0 < out->opts->dump_opts.before_size) {
361
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
362
+ out->cur += out->opts->dump_opts.before_size;
363
+ }
364
+ *out->cur++ = ':';
365
+ if (0 < out->opts->dump_opts.after_size) {
366
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
367
+ out->cur += out->opts->dump_opts.after_size;
368
+ }
369
+ dump_array(rb_funcall(obj, backtrace_id, 0), depth, out, false);
370
+ fill_indent(out, depth);
371
+ *out->cur++ = '}';
372
+ *out->cur = '\0';
373
+ }
374
+
375
+ static ID table_id = 0;
376
+
377
+ static void
378
+ openstruct_alt(VALUE obj, int depth, Out out) {
379
+ struct _attr attrs[] = {
380
+ { "t", 1, Qnil },
381
+ { NULL, 0, Qnil },
382
+ };
383
+ if (0 == table_id) {
384
+ table_id = rb_intern("table");
385
+ }
386
+ attrs[0].value = rb_funcall(obj, table_id, 0);
387
+
388
+ oj_code_attrs(obj, attrs, depth, out, true);
389
+ }
390
+
391
+ static void
392
+ range_alt(VALUE obj, int depth, Out out) {
393
+ int d3 = depth + 2;
394
+ size_t size = d3 * out->indent + 2;
395
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
396
+ VALUE args[] = { Qundef, Qundef, Qundef, Qundef };
397
+
398
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
399
+
400
+ assure_size(out, size + sep_len + 6);
401
+ *out->cur++ = ',';
402
+ fill_indent(out, d3);
403
+ *out->cur++ = '"';
404
+ *out->cur++ = 'a';
405
+ *out->cur++ = '"';
406
+ if (0 < out->opts->dump_opts.before_size) {
407
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
408
+ out->cur += out->opts->dump_opts.before_size;
409
+ }
410
+ *out->cur++ = ':';
411
+ if (0 < out->opts->dump_opts.after_size) {
412
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
413
+ out->cur += out->opts->dump_opts.after_size;
414
+ }
415
+ args[0] = rb_funcall(obj, oj_begin_id, 0);
416
+ args[1] = rb_funcall(obj, oj_end_id, 0);
417
+ args[2] = rb_funcall(obj, oj_exclude_end_id, 0);
418
+ dump_values_array(args, depth, out);
419
+ fill_indent(out, depth);
420
+ *out->cur++ = '}';
421
+ *out->cur = '\0';
422
+ }
423
+
424
+ static ID numerator_id = 0;
425
+ static ID denominator_id = 0;
426
+
427
+ static void
428
+ rational_alt(VALUE obj, int depth, Out out) {
429
+ struct _attr attrs[] = {
430
+ { "n", 1, Qnil },
431
+ { "d", 1, Qnil },
432
+ { NULL, 0, Qnil },
433
+ };
434
+ if (0 == numerator_id) {
435
+ numerator_id = rb_intern("numerator");
436
+ denominator_id = rb_intern("denominator");
437
+ }
438
+ attrs[0].value = rb_funcall(obj, numerator_id, 0);
439
+ attrs[1].value = rb_funcall(obj, denominator_id, 0);
440
+
441
+ oj_code_attrs(obj, attrs, depth, out, true);
442
+ }
443
+
444
+ static ID options_id = 0;
445
+ static ID source_id = 0;
446
+
447
+ static void
448
+ regexp_alt(VALUE obj, int depth, Out out) {
449
+ struct _attr attrs[] = {
450
+ { "o", 1, Qnil },
451
+ { "s", 1, Qnil },
452
+ { NULL, 0, Qnil },
453
+ };
454
+ if (0 == options_id) {
455
+ options_id = rb_intern("options");
456
+ source_id = rb_intern("source");
457
+ }
458
+ attrs[0].value = rb_funcall(obj, options_id, 0);
459
+ attrs[1].value = rb_funcall(obj, source_id, 0);
460
+
461
+ oj_code_attrs(obj, attrs, depth, out, true);
462
+ }
463
+
464
+ static void
465
+ time_alt(VALUE obj, int depth, Out out) {
466
+ struct _attr attrs[] = {
467
+ { "s", 1, Qundef, 0, Qundef },
468
+ { "n", 1, Qundef, 0, Qundef },
469
+ { NULL, 0, Qnil },
470
+ };
471
+ time_t sec;
472
+ long long nsec;
473
+
474
+ #ifdef HAVE_RB_TIME_TIMESPEC
475
+ if (16 <= sizeof(struct timespec)) {
476
+ struct timespec ts = rb_time_timespec(obj);
477
+
478
+ sec = (long long)ts.tv_sec;
479
+ nsec = ts.tv_nsec;
480
+ } else {
481
+ sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
482
+ nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
483
+ }
484
+ #else
485
+ sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
486
+ nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
487
+ #endif
488
+
489
+ attrs[0].num = sec;
490
+ attrs[1].num = nsec;
491
+
492
+ oj_code_attrs(obj, attrs, depth, out, true);
493
+ }
494
+
495
+ struct _code oj_compat_codes[] = {
496
+ { "BigDecimal", Qnil, bigdecimal_alt, NULL, false },
497
+ { "Complex", Qnil, complex_alt, NULL, false },
498
+ { "Date", Qnil, date_alt, false },
499
+ { "DateTime", Qnil, datetime_alt, NULL, false },
500
+ { "OpenStruct", Qnil, openstruct_alt, NULL, false },
501
+ { "Range", Qnil, range_alt, NULL, false },
502
+ { "Rational", Qnil, rational_alt, NULL, false },
503
+ { "Regexp", Qnil, regexp_alt, NULL, false },
504
+ { "Time", Qnil, time_alt, NULL, false },
505
+ // TBD the rest of the library classes
506
+ { NULL, Qundef, NULL, NULL, false },
507
+ };
508
+
509
+ VALUE
510
+ oj_add_to_json(int argc, VALUE *argv, VALUE self) {
511
+ Code a;
512
+
513
+ if (0 == argc) {
514
+ for (a = oj_compat_codes; NULL != a->name; a++) {
515
+ if (Qnil == a->clas || Qundef == a->clas) {
516
+ a->clas = rb_const_get_at(rb_cObject, rb_intern(a->name));
517
+ }
518
+ a->active = true;
519
+ }
520
+ use_struct_alt = true;
521
+ use_exception_alt = true;
522
+ use_bignum_alt = true;
523
+ oj_use_hash_alt = true;
524
+ oj_use_array_alt = true;
525
+ } else {
526
+ for (; 0 < argc; argc--, argv++) {
527
+ if (rb_cStruct == *argv) {
528
+ use_struct_alt = true;
529
+ continue;
530
+ }
531
+ if (rb_eException == *argv) {
532
+ use_exception_alt = true;
533
+ continue;
534
+ }
535
+ if (rb_cInteger == *argv) {
536
+ use_bignum_alt = true;
537
+ continue;
538
+ }
539
+ if (rb_cHash == *argv) {
540
+ oj_use_hash_alt = true;
541
+ continue;
542
+ }
543
+ if (rb_cArray == *argv) {
544
+ oj_use_array_alt = true;
545
+ continue;
546
+ }
547
+ for (a = oj_compat_codes; NULL != a->name; a++) {
548
+ if (Qnil == a->clas || Qundef == a->clas) {
549
+ a->clas = rb_const_get_at(rb_cObject, rb_intern(a->name));
550
+ }
551
+ if (*argv == a->clas) {
552
+ a->active = true;
553
+ break;
554
+ }
555
+ }
556
+ }
557
+ }
558
+ return Qnil;
559
+ }
560
+
561
+ VALUE
562
+ oj_remove_to_json(int argc, VALUE *argv, VALUE self) {
563
+ if (0 == argc) {
564
+ oj_code_set_active(oj_compat_codes, Qnil, false);
565
+ use_struct_alt = false;
566
+ use_exception_alt = false;
567
+ use_bignum_alt = false;
568
+ oj_use_hash_alt = false;
569
+ oj_use_array_alt = false;
570
+ } else {
571
+ for (; 0 < argc; argc--, argv++) {
572
+ if (rb_cStruct == *argv) {
573
+ use_struct_alt = false;
574
+ continue;
575
+ }
576
+ if (rb_eException == *argv) {
577
+ use_exception_alt = false;
578
+ continue;
579
+ }
580
+ if (rb_cInteger == *argv) {
581
+ use_bignum_alt = false;
582
+ continue;
583
+ }
584
+ if (rb_cHash == *argv) {
585
+ oj_use_hash_alt = false;
586
+ continue;
587
+ }
588
+ if (rb_cArray == *argv) {
589
+ oj_use_array_alt = false;
590
+ continue;
591
+ }
592
+ oj_code_set_active(oj_compat_codes, *argv, false);
593
+ }
594
+ }
595
+ return Qnil;
596
+ }
597
+
598
+ // The JSON gem is inconsistent with handling of infinity. Using
599
+ // JSON.dump(0.1/0) returns the string Infinity but (0.1/0).to_json raise and
600
+ // exception. Worse, for BigDecimals a quoted "Infinity" is returned.
601
+ static void
602
+ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
603
+ char buf[64];
604
+ char *b;
605
+ double d = rb_num2dbl(obj);
606
+ int cnt = 0;
607
+
608
+ if (0.0 == d) {
609
+ b = buf;
610
+ *b++ = '0';
611
+ *b++ = '.';
612
+ *b++ = '0';
613
+ *b++ = '\0';
614
+ cnt = 3;
615
+ } else if (OJ_INFINITY == d) {
616
+ if (WordNan == out->opts->dump_opts.nan_dump) {
617
+ strcpy(buf, "Infinity");
618
+ } else {
619
+ raise_json_err("Infinity not allowed in JSON.", "GeneratorError");
620
+ }
621
+ } else if (-OJ_INFINITY == d) {
622
+ if (WordNan == out->opts->dump_opts.nan_dump) {
623
+ strcpy(buf, "-Infinity");
624
+ } else {
625
+ raise_json_err("-Infinity not allowed in JSON.", "GeneratorError");
626
+ }
627
+ } else if (isnan(d)) {
628
+ if (WordNan == out->opts->dump_opts.nan_dump) {
629
+ strcpy(buf, "NaN");
630
+ } else {
631
+ raise_json_err("NaN not allowed in JSON.", "GeneratorError");
632
+ }
633
+ } else if (d == (double)(long long int)d) {
634
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
635
+ } else if (oj_rails_float_opt) {
636
+ cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, "%0.16g");
637
+ } else {
638
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
639
+
640
+ strcpy(buf, rb_string_value_ptr((VALUE*)&rstr));
641
+ cnt = (int)RSTRING_LEN(rstr);
642
+ }
643
+ assure_size(out, cnt);
644
+ for (b = buf; '\0' != *b; b++) {
645
+ *out->cur++ = *b;
646
+ }
647
+ *out->cur = '\0';
648
+ }
649
+
650
+ static int
651
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
652
+ Out out = (Out)ov;
653
+ int depth = out->depth;
654
+
655
+ if (out->omit_nil && Qnil == value) {
656
+ return ST_CONTINUE;
657
+ }
658
+ if (!out->opts->dump_opts.use) {
659
+ assure_size(out, depth * out->indent + 1);
660
+ fill_indent(out, depth);
661
+ } else {
662
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
663
+ if (0 < out->opts->dump_opts.hash_size) {
664
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
665
+ out->cur += out->opts->dump_opts.hash_size;
666
+ }
667
+ if (0 < out->opts->dump_opts.indent_size) {
668
+ int i;
669
+ for (i = depth; 0 < i; i--) {
670
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
671
+ out->cur += out->opts->dump_opts.indent_size;
672
+ }
673
+ }
674
+ }
675
+ switch (rb_type(key)) {
676
+ case T_STRING:
677
+ oj_dump_str(key, 0, out, false);
678
+ break;
679
+ case T_SYMBOL:
680
+ oj_dump_sym(key, 0, out, false);
681
+ break;
682
+ default:
683
+ /*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
684
+ oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false);
685
+ break;
686
+ }
687
+ if (!out->opts->dump_opts.use) {
688
+ *out->cur++ = ':';
689
+ } else {
690
+ assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
691
+ if (0 < out->opts->dump_opts.before_size) {
692
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
693
+ out->cur += out->opts->dump_opts.before_size;
694
+ }
695
+ *out->cur++ = ':';
696
+ if (0 < out->opts->dump_opts.after_size) {
697
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
698
+ out->cur += out->opts->dump_opts.after_size;
699
+ }
700
+ }
701
+ oj_dump_compat_val(value, depth, out, true);
702
+ out->depth = depth;
703
+ *out->cur++ = ',';
704
+
705
+ return ST_CONTINUE;
706
+ }
707
+
708
+ static void
709
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
710
+ int cnt;
711
+ long id = oj_check_circular(obj, out);
712
+
713
+ if (0 > id) {
714
+ raise_json_err("Too deeply nested", "NestingError");
715
+ return;
716
+ }
717
+ if (as_ok && !oj_use_hash_alt && rb_obj_class(obj) != rb_cHash && rb_respond_to(obj, oj_to_json_id)) {
718
+ dump_to_json(obj, out);
719
+ return;
720
+ }
721
+ cnt = (int)RHASH_SIZE(obj);
722
+ assure_size(out, 2);
723
+ if (0 == cnt) {
724
+ *out->cur++ = '{';
725
+ *out->cur++ = '}';
726
+ } else {
727
+ *out->cur++ = '{';
728
+ out->depth = depth + 1;
729
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
730
+ if (',' == *(out->cur - 1)) {
731
+ out->cur--; // backup to overwrite last comma
732
+ }
733
+ if (!out->opts->dump_opts.use) {
734
+ assure_size(out, depth * out->indent + 2);
735
+ fill_indent(out, depth);
736
+ } else {
737
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
738
+ if (0 < out->opts->dump_opts.hash_size) {
739
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
740
+ out->cur += out->opts->dump_opts.hash_size;
741
+ }
742
+ if (0 < out->opts->dump_opts.indent_size) {
743
+ int i;
744
+
745
+ for (i = depth; 0 < i; i--) {
746
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
747
+ out->cur += out->opts->dump_opts.indent_size;
748
+ }
749
+ }
750
+ }
751
+ *out->cur++ = '}';
752
+ }
753
+ *out->cur = '\0';
754
+ }
755
+
756
+ // In compat mode only the first call check for to_json. After that to_s is
757
+ // called.
758
+ static void
759
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
760
+ if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
761
+ return;
762
+ }
763
+ if (use_exception_alt && rb_obj_is_kind_of(obj, rb_eException)) {
764
+ exception_alt(obj, depth, out);
765
+ return;
766
+ }
767
+ if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
768
+ oj_dump_raw_json(obj, depth, out);
769
+ return;
770
+ }
771
+ if (as_ok && rb_respond_to(obj, oj_to_json_id)) {
772
+ dump_to_json(obj, out);
773
+ return;
774
+ }
775
+ // Nothing else matched so encode as a JSON object with Ruby obj members
776
+ // as JSON object members.
777
+ oj_dump_obj_to_s(obj, out);
778
+ }
779
+
780
+ static void
781
+ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
782
+ VALUE clas = rb_obj_class(obj);
783
+
784
+ if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
785
+ return;
786
+ }
787
+ if (rb_cRange == clas) {
788
+ *out->cur++ = '"';
789
+ oj_dump_compat_val(rb_funcall(obj, oj_begin_id, 0), 0, out, false);
790
+ assure_size(out, 3);
791
+ *out->cur++ = '.';
792
+ *out->cur++ = '.';
793
+ if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
794
+ *out->cur++ = '.';
795
+ }
796
+ oj_dump_compat_val(rb_funcall(obj, oj_end_id, 0), 0, out, false);
797
+ *out->cur++ = '"';
798
+
799
+ return;
800
+ }
801
+ if (as_ok && rb_respond_to(obj, oj_to_json_id)) {
802
+ dump_to_json(obj, out);
803
+
804
+ return;
805
+ }
806
+ if (use_struct_alt) {
807
+ int d3 = depth + 2;
808
+ size_t size = d3 * out->indent + 2;
809
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
810
+ const char *classname = rb_class2name(rb_obj_class(obj));
811
+ VALUE args[100];
812
+ int cnt;
813
+ int i;
814
+
815
+ if (NULL == classname || '#' == *classname) {
816
+ raise_json_err("Only named structs are supported.", "JSONError");
817
+ }
818
+ #ifdef RSTRUCT_LEN
819
+ #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
820
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
821
+ #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
822
+ cnt = (int)RSTRUCT_LEN(obj);
823
+ #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
824
+ #else
825
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
826
+ // class in interpreted Ruby so length() may not be defined.
827
+ cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
828
+ #endif
829
+ if (sizeof(args) / sizeof(*args) <= (size_t)cnt) {
830
+ // TBD allocate and try again
831
+ cnt = 99;
832
+ }
833
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
834
+
835
+ assure_size(out, size + sep_len + 6);
836
+ *out->cur++ = ',';
837
+ fill_indent(out, d3);
838
+ *out->cur++ = '"';
839
+ *out->cur++ = 'v';
840
+ *out->cur++ = '"';
841
+ if (0 < out->opts->dump_opts.before_size) {
842
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
843
+ out->cur += out->opts->dump_opts.before_size;
844
+ }
845
+ *out->cur++ = ':';
846
+ if (0 < out->opts->dump_opts.after_size) {
847
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
848
+ out->cur += out->opts->dump_opts.after_size;
849
+ }
850
+ for (i = 0; i < cnt; i++) {
851
+ #ifdef RSTRUCT_LEN
852
+ args[i] = RSTRUCT_GET(obj, i);
853
+ #else
854
+ args[i] = rb_struct_aref(obj, INT2FIX(i));
855
+ #endif
856
+ }
857
+ args[cnt] = Qundef;
858
+ dump_values_array(args, depth, out);
859
+ fill_indent(out, depth);
860
+ *out->cur++ = '}';
861
+ *out->cur = '\0';
862
+ } else {
863
+ oj_dump_obj_to_s(obj, out);
864
+ }
865
+ }
866
+
867
+ static void
868
+ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
869
+ // The json gem uses to_s explicitly. to_s can be overridden while
870
+ // rb_big2str can not so unless overridden by using add_to_json(Integer)
871
+ // this must use to_s to pass the json gem unit tests.
872
+ volatile VALUE rs;
873
+ int cnt;
874
+ bool dump_as_string = false;
875
+
876
+ if (use_bignum_alt) {
877
+ rs = rb_big2str(obj, 10);
878
+ } else {
879
+ rs = rb_funcall(obj, oj_to_s_id, 0);
880
+ }
881
+ rb_check_type(rs, T_STRING);
882
+ cnt = (int)RSTRING_LEN(rs);
883
+
884
+ if (out->opts->int_range_min != 0 || out->opts->int_range_max != 0) {
885
+ dump_as_string = true; // Bignum cannot be inside of Fixnum range
886
+ assure_size(out, cnt + 2);
887
+ *out->cur++ = '"';
888
+ } else {
889
+ assure_size(out, cnt);
890
+ }
891
+ memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
892
+ out->cur += cnt;
893
+ if (dump_as_string) {
894
+ *out->cur++ = '"';
895
+ }
896
+ *out->cur = '\0';
897
+ }
898
+
899
+ static DumpFunc compat_funcs[] = {
900
+ NULL, // RUBY_T_NONE = 0x00,
901
+ dump_obj, // RUBY_T_OBJECT = 0x01,
902
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
903
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
904
+ dump_float, // RUBY_T_FLOAT = 0x04,
905
+ oj_dump_str, // RUBY_T_STRING = 0x05,
906
+ dump_obj, // RUBY_T_REGEXP = 0x06,
907
+ dump_array, // RUBY_T_ARRAY = 0x07,
908
+ dump_hash, // RUBY_T_HASH = 0x08,
909
+ dump_struct, // RUBY_T_STRUCT = 0x09,
910
+ dump_bignum, // RUBY_T_BIGNUM = 0x0a,
911
+ NULL, // RUBY_T_FILE = 0x0b,
912
+ dump_obj, // RUBY_T_DATA = 0x0c,
913
+ NULL, // RUBY_T_MATCH = 0x0d,
914
+ dump_obj, // RUBY_T_COMPLEX = 0x0e,
915
+ dump_obj, // RUBY_T_RATIONAL = 0x0f,
916
+ NULL, // 0x10
917
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
918
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
919
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
920
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
921
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
922
+ };
923
+
924
+ static void
925
+ set_state_depth(VALUE state, int depth) {
926
+ VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
927
+ VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
928
+ VALUE generator = rb_const_get(ext, rb_intern("Generator"));
929
+ VALUE state_class = rb_const_get(generator, rb_intern("State"));
930
+
931
+ if (state_class == rb_obj_class(state)) {
932
+ rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
933
+ }
934
+ }
935
+
936
+ void
937
+ oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
938
+ int type = rb_type(obj);
939
+
940
+ if (Yes == out->opts->trace) {
941
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
942
+ }
943
+ if (out->opts->dump_opts.max_depth <= depth) {
944
+ // When JSON.dump is called then an ArgumentError is expected and the
945
+ // limit is the depth inclusive. If JSON.generate is called then a
946
+ // NestingError is expected and the limit is inclusive. Worse than
947
+ // that there are unit tests for both.
948
+ if (CALLER_DUMP == out->caller) {
949
+ if (0 < out->argc) {
950
+ set_state_depth(*out->argv, depth);
951
+ }
952
+ rb_raise(rb_eArgError, "Too deeply nested.");
953
+ } else if (out->opts->dump_opts.max_depth < depth) {
954
+ if (0 < out->argc) {
955
+ set_state_depth(*out->argv, depth - 1);
956
+ }
957
+ raise_json_err("Too deeply nested", "NestingError");
958
+ }
959
+ }
960
+ if (0 < type && type <= RUBY_T_FIXNUM) {
961
+ DumpFunc f = compat_funcs[type];
962
+
963
+ if (NULL != f) {
964
+ f(obj, depth, out, as_ok);
965
+ if (Yes == out->opts->trace) {
966
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
967
+ }
968
+ return;
969
+ }
970
+ }
971
+ oj_dump_nil(Qnil, depth, out, false);
972
+ if (Yes == out->opts->trace) {
973
+ oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
974
+ }
975
+ }