oj 3.10.7

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 (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,112 @@
1
+ /* parse.h
2
+ * Copyright (c) 2011, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #ifndef OJ_PARSE_H
7
+ #define OJ_PARSE_H
8
+
9
+ #include <stdarg.h>
10
+ #include <stdio.h>
11
+ #include <string.h>
12
+
13
+ #include "ruby.h"
14
+ #include "oj.h"
15
+ #include "val_stack.h"
16
+ #include "circarray.h"
17
+ #include "reader.h"
18
+ #include "rxclass.h"
19
+
20
+ struct _rxClass;
21
+
22
+ typedef struct _numInfo {
23
+ int64_t i;
24
+ int64_t num;
25
+ int64_t div;
26
+ int64_t di;
27
+ const char *str;
28
+ size_t len;
29
+ long exp;
30
+ int big;
31
+ int infinity;
32
+ int nan;
33
+ int neg;
34
+ int hasExp;
35
+ int no_big;
36
+ } *NumInfo;
37
+
38
+ typedef struct _parseInfo {
39
+ // used for the string parser
40
+ const char *json;
41
+ const char *cur;
42
+ const char *end;
43
+ // used for the stream parser
44
+ struct _reader rd;
45
+
46
+ struct _err err;
47
+ struct _options options;
48
+ VALUE handler;
49
+ struct _valStack stack;
50
+ CircArray circ_array;
51
+ struct _rxClass str_rx;
52
+ int expect_value;
53
+ int max_depth; // just for the json gem
54
+ VALUE proc;
55
+ VALUE (*start_hash)(struct _parseInfo *pi);
56
+ void (*end_hash)(struct _parseInfo *pi);
57
+ VALUE (*hash_key)(struct _parseInfo *pi, const char *key, size_t klen);
58
+ void (*hash_set_cstr)(struct _parseInfo *pi, Val kval, const char *str, size_t len, const char *orig);
59
+ void (*hash_set_num)(struct _parseInfo *pi, Val kval, NumInfo ni);
60
+ void (*hash_set_value)(struct _parseInfo *pi, Val kval, VALUE value);
61
+
62
+ VALUE (*start_array)(struct _parseInfo *pi);
63
+ void (*end_array)(struct _parseInfo *pi);
64
+ void (*array_append_cstr)(struct _parseInfo *pi, const char *str, size_t len, const char *orig);
65
+ void (*array_append_num)(struct _parseInfo *pi, NumInfo ni);
66
+ void (*array_append_value)(struct _parseInfo *pi, VALUE value);
67
+
68
+ void (*add_cstr)(struct _parseInfo *pi, const char *str, size_t len, const char *orig);
69
+ void (*add_num)(struct _parseInfo *pi, NumInfo ni);
70
+ void (*add_value)(struct _parseInfo *pi, VALUE val);
71
+ VALUE err_class;
72
+ bool has_callbacks;
73
+ } *ParseInfo;
74
+
75
+ extern void oj_parse2(ParseInfo pi);
76
+ extern void oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const char *format, ...);
77
+ extern VALUE oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk);
78
+ extern VALUE oj_num_as_value(NumInfo ni);
79
+
80
+ extern void oj_set_strict_callbacks(ParseInfo pi);
81
+ extern void oj_set_object_callbacks(ParseInfo pi);
82
+ extern void oj_set_compat_callbacks(ParseInfo pi);
83
+ extern void oj_set_custom_callbacks(ParseInfo pi);
84
+ extern void oj_set_wab_callbacks(ParseInfo pi);
85
+
86
+ extern void oj_sparse2(ParseInfo pi);
87
+ extern VALUE oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd);
88
+
89
+ static inline void
90
+ parse_info_init(ParseInfo pi) {
91
+ memset(pi, 0, sizeof(struct _parseInfo));
92
+ }
93
+
94
+ static inline bool
95
+ empty_ok(Options options) {
96
+ switch (options->mode) {
97
+ case ObjectMode:
98
+ case WabMode:
99
+ return true;
100
+ case CompatMode:
101
+ case RailsMode:
102
+ return false;
103
+ case StrictMode:
104
+ case NullMode:
105
+ case CustomMode:
106
+ default:
107
+ break;
108
+ }
109
+ return Yes == options->empty_string;
110
+ }
111
+
112
+ #endif /* OJ_PARSE_H */
@@ -0,0 +1,1528 @@
1
+ /* rails.c
2
+ * Copyright (c) 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include "rails.h"
7
+ #include "encode.h"
8
+ #include "code.h"
9
+ #include "encode.h"
10
+ #include "trace.h"
11
+ #include "util.h"
12
+
13
+ #define OJ_INFINITY (1.0/0.0)
14
+
15
+ // TBD keep static array of strings and functions to help with rails optimization
16
+ typedef struct _encoder {
17
+ struct _rOptTable ropts;
18
+ struct _options opts;
19
+ VALUE arg;
20
+ } *Encoder;
21
+
22
+ bool oj_rails_hash_opt = false;
23
+ bool oj_rails_array_opt = false;
24
+ bool oj_rails_float_opt = false;
25
+
26
+ extern void oj_mimic_json_methods(VALUE json);
27
+
28
+ static void dump_rails_val(VALUE obj, int depth, Out out, bool as_ok);
29
+
30
+ extern VALUE Oj;
31
+
32
+ static struct _rOptTable ropts = { 0, 0, NULL };
33
+
34
+ static VALUE encoder_class = Qnil;
35
+ static bool escape_html = true;
36
+ static bool xml_time = true;
37
+
38
+ static ROpt create_opt(ROptTable rot, VALUE clas);
39
+
40
+ ROpt
41
+ oj_rails_get_opt(ROptTable rot, VALUE clas) {
42
+ if (NULL == rot) {
43
+ rot = &ropts;
44
+ }
45
+ if (0 < rot->len) {
46
+ int lo = 0;
47
+ int hi = rot->len - 1;
48
+ int mid;
49
+ VALUE v;
50
+
51
+ if (clas < rot->table->clas || rot->table[hi].clas < clas) {
52
+ return NULL;
53
+ }
54
+ if (rot->table[lo].clas == clas) {
55
+ return rot->table;
56
+ }
57
+ if (rot->table[hi].clas == clas) {
58
+ return &rot->table[hi];
59
+ }
60
+ while (2 <= hi - lo) {
61
+ mid = (hi + lo) / 2;
62
+ v = rot->table[mid].clas;
63
+ if (v == clas) {
64
+ return &rot->table[mid];
65
+ }
66
+ if (v < clas) {
67
+ lo = mid;
68
+ } else {
69
+ hi = mid;
70
+ }
71
+ }
72
+ }
73
+ return NULL;
74
+ }
75
+
76
+ static ROptTable
77
+ copy_opts(ROptTable src, ROptTable dest) {
78
+ dest->len = src->len;
79
+ dest->alen = src->alen;
80
+ if (NULL == src->table) {
81
+ dest->table = NULL;
82
+ } else {
83
+ dest->table = ALLOC_N(struct _rOpt, dest->alen);
84
+ memcpy(dest->table, src->table, sizeof(struct _rOpt) * dest->alen);
85
+ }
86
+ return NULL;
87
+ }
88
+
89
+ static int
90
+ dump_attr_cb(ID key, VALUE value, VALUE ov) {
91
+ Out out = (Out)ov;
92
+ int depth = out->depth;
93
+ size_t size = depth * out->indent + 1;
94
+ const char *attr = rb_id2name(key);
95
+
96
+ // Some exceptions such as NoMethodError have an invisible attribute where
97
+ // the key name is NULL. Not an empty string but NULL.
98
+ if (NULL == attr) {
99
+ attr = "";
100
+ }
101
+ if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
102
+ return ST_CONTINUE;
103
+ }
104
+ assure_size(out, size);
105
+ fill_indent(out, depth);
106
+ if ('@' == *attr) {
107
+ attr++;
108
+ oj_dump_cstr(attr, strlen(attr), 0, 0, out);
109
+ } else {
110
+ char buf[32];
111
+
112
+ *buf = '~';
113
+ strncpy(buf + 1, attr, sizeof(buf) - 2);
114
+ buf[sizeof(buf) - 1] = '\0';
115
+ oj_dump_cstr(buf, strlen(buf), 0, 0, out);
116
+ }
117
+ *out->cur++ = ':';
118
+ dump_rails_val(value, depth, out, true);
119
+ out->depth = depth;
120
+ *out->cur++ = ',';
121
+
122
+ return ST_CONTINUE;
123
+ }
124
+
125
+ static void
126
+ dump_obj_attrs(VALUE obj, int depth, Out out, bool as_ok) {
127
+ assure_size(out, 2);
128
+ *out->cur++ = '{';
129
+ out->depth = depth + 1;
130
+ rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
131
+ if (',' == *(out->cur - 1)) {
132
+ out->cur--; // backup to overwrite last comma
133
+ }
134
+ out->depth = depth;
135
+ fill_indent(out, depth);
136
+ *out->cur++ = '}';
137
+ *out->cur = '\0';
138
+ }
139
+
140
+ static void
141
+ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
142
+ int d3 = depth + 2;
143
+ size_t size = d3 * out->indent + 2;
144
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
145
+ volatile VALUE ma;
146
+ volatile VALUE v;
147
+ int cnt;
148
+ int i;
149
+ int len;
150
+ const char *name;
151
+
152
+ #ifdef RSTRUCT_LEN
153
+ #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
154
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
155
+ #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
156
+ cnt = (int)RSTRUCT_LEN(obj);
157
+ #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
158
+ #else
159
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
160
+ // class in interpreted Ruby so length() may not be defined.
161
+ cnt = FIX2INT(rb_funcall(obj, oj_length_id, 0));
162
+ #endif
163
+ ma = rb_struct_s_members(rb_obj_class(obj));
164
+ assure_size(out, 2);
165
+ *out->cur++ = '{';
166
+ for (i = 0; i < cnt; i++) {
167
+ volatile VALUE s = rb_sym_to_s(rb_ary_entry(ma, i));
168
+
169
+ name = rb_string_value_ptr((VALUE*)&s);
170
+ len = (int)RSTRING_LEN(s);
171
+ assure_size(out, size + sep_len + 6);
172
+ if (0 < i) {
173
+ *out->cur++ = ',';
174
+ }
175
+ fill_indent(out, d3);
176
+ *out->cur++ = '"';
177
+ memcpy(out->cur, name, len);
178
+ out->cur += len;
179
+ *out->cur++ = '"';
180
+ if (0 < out->opts->dump_opts.before_size) {
181
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
182
+ out->cur += out->opts->dump_opts.before_size;
183
+ }
184
+ *out->cur++ = ':';
185
+ if (0 < out->opts->dump_opts.after_size) {
186
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
187
+ out->cur += out->opts->dump_opts.after_size;
188
+ }
189
+ #ifdef RSTRUCT_LEN
190
+ v = RSTRUCT_GET(obj, i);
191
+ #else
192
+ v = rb_struct_aref(obj, INT2FIX(i));
193
+ #endif
194
+ dump_rails_val(v, d3, out, true);
195
+ }
196
+ fill_indent(out, depth);
197
+ *out->cur++ = '}';
198
+ *out->cur = '\0';
199
+ }
200
+
201
+ static ID to_a_id = 0;
202
+
203
+ static void
204
+ dump_enumerable(VALUE obj, int depth, Out out, bool as_ok) {
205
+ if (0 == to_a_id) {
206
+ to_a_id = rb_intern("to_a");
207
+ }
208
+ dump_rails_val(rb_funcall(obj, to_a_id, 0), depth, out, false);
209
+ }
210
+
211
+ static void
212
+ dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
213
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
214
+ const char *str = rb_string_value_ptr((VALUE*)&rstr);
215
+
216
+ if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
217
+ oj_dump_nil(Qnil, depth, out, false);
218
+ } else if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) {
219
+ oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
220
+ } else if (Yes == out->opts->bigdec_as_num) {
221
+ oj_dump_raw(str, (int)RSTRING_LEN(rstr), out);
222
+ } else {
223
+ oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
224
+ }
225
+ }
226
+
227
+ static void
228
+ dump_sec_nano(VALUE obj, int64_t sec, long nsec, Out out) {
229
+ char buf[64];
230
+ struct _timeInfo ti;
231
+ long one = 1000000000;
232
+ long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
233
+ int tzhour, tzmin;
234
+ char tzsign = '+';
235
+ int len;
236
+
237
+ if (out->end - out->cur <= 36) {
238
+ assure_size(out, 36);
239
+ }
240
+ if (9 > out->opts->sec_prec) {
241
+ int i;
242
+
243
+ // Rails does not round when reducing precision but instead floors,
244
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
245
+ nsec = nsec / 10;
246
+ one /= 10;
247
+ }
248
+ if (one <= nsec) {
249
+ nsec -= one;
250
+ sec++;
251
+ }
252
+ }
253
+ // 2012-01-05T23:58:07.123456000+09:00 or 2012/01/05 23:58:07 +0900
254
+ sec += tzsecs;
255
+ sec_as_time(sec, &ti);
256
+ if (0 > tzsecs) {
257
+ tzsign = '-';
258
+ tzhour = (int)(tzsecs / -3600);
259
+ tzmin = (int)(tzsecs / -60) - (tzhour * 60);
260
+ } else {
261
+ tzhour = (int)(tzsecs / 3600);
262
+ tzmin = (int)(tzsecs / 60) - (tzhour * 60);
263
+ }
264
+ if (!xml_time) {
265
+ len = sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d %c%02d%02d", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, tzsign, tzhour, tzmin);
266
+ } else if (0 == out->opts->sec_prec) {
267
+ if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
268
+ len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
269
+ } else {
270
+ len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, tzsign, tzhour, tzmin);
271
+ }
272
+ } else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
273
+ char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
274
+
275
+ len = 30;
276
+ if (9 > out->opts->sec_prec) {
277
+ format[32] = '0' + out->opts->sec_prec;
278
+ len -= 9 - out->opts->sec_prec;
279
+ }
280
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, nsec);
281
+ } else {
282
+ char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
283
+
284
+ len = 35;
285
+ if (9 > out->opts->sec_prec) {
286
+ format[32] = '0' + out->opts->sec_prec;
287
+ len -= 9 - out->opts->sec_prec;
288
+ }
289
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, nsec, tzsign, tzhour, tzmin);
290
+ }
291
+ oj_dump_cstr(buf, len, 0, 0, out);
292
+ }
293
+
294
+ static void
295
+ dump_time(VALUE obj, int depth, Out out, bool as_ok) {
296
+ long long sec;
297
+ long long nsec;
298
+
299
+ #ifdef HAVE_RB_TIME_TIMESPEC
300
+ if (16 <= sizeof(struct timespec)) {
301
+ struct timespec ts = rb_time_timespec(obj);
302
+
303
+ sec = (long long)ts.tv_sec;
304
+ nsec = ts.tv_nsec;
305
+ } else {
306
+ sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
307
+ nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
308
+ }
309
+ #else
310
+ sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
311
+ nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
312
+ #endif
313
+ dump_sec_nano(obj, sec, nsec, out);
314
+ }
315
+
316
+ static void
317
+ dump_timewithzone(VALUE obj, int depth, Out out, bool as_ok) {
318
+ int64_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
319
+ long long nsec = 0;
320
+
321
+ if (rb_respond_to(obj, oj_tv_nsec_id)) {
322
+ nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
323
+ } else if (rb_respond_to(obj, oj_tv_usec_id)) {
324
+ nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
325
+ }
326
+ dump_sec_nano(obj, sec, nsec, out);
327
+ }
328
+
329
+ static void
330
+ dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
331
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
332
+
333
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
334
+ }
335
+
336
+ static ID parameters_id = 0;
337
+
338
+ typedef struct _strLen {
339
+ const char *str;
340
+ int len;
341
+ } *StrLen;
342
+
343
+ static void
344
+ dump_actioncontroller_parameters(VALUE obj, int depth, Out out, bool as_ok) {
345
+ if (0 == parameters_id) {
346
+ parameters_id = rb_intern("@parameters");
347
+ }
348
+ out->argc = 0;
349
+ dump_rails_val(rb_ivar_get(obj, parameters_id), depth, out, true);
350
+ }
351
+
352
+ static StrLen
353
+ columns_array(VALUE rcols, int *ccnt) {
354
+ volatile VALUE v;
355
+ StrLen cp;
356
+ StrLen cols;
357
+ int i;
358
+ int cnt = (int)RARRAY_LEN(rcols);
359
+
360
+ *ccnt = cnt;
361
+ cols = ALLOC_N(struct _strLen, cnt);
362
+ for (i = 0, cp = cols; i < cnt; i++, cp++) {
363
+ v = rb_ary_entry(rcols, i);
364
+ if (T_STRING != rb_type(v)) {
365
+ v = rb_funcall(v, oj_to_s_id, 0);
366
+ }
367
+ cp->str = StringValuePtr(v);
368
+ cp->len = (int)RSTRING_LEN(v);
369
+ }
370
+ return cols;
371
+ }
372
+
373
+ static void
374
+ dump_row(VALUE row, StrLen cols, int ccnt, int depth, Out out) {
375
+ size_t size;
376
+ int d2 = depth + 1;
377
+ int i;
378
+
379
+ assure_size(out, 2);
380
+ *out->cur++ = '{';
381
+ size = depth * out->indent + 3;
382
+ for (i = 0; i < ccnt; i++, cols++) {
383
+ assure_size(out, size);
384
+ if (out->opts->dump_opts.use) {
385
+ if (0 < out->opts->dump_opts.array_size) {
386
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
387
+ out->cur += out->opts->dump_opts.array_size;
388
+ }
389
+ if (0 < out->opts->dump_opts.indent_size) {
390
+ int i;
391
+ for (i = d2; 0 < i; i--) {
392
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
393
+ out->cur += out->opts->dump_opts.indent_size;
394
+ }
395
+ }
396
+ } else {
397
+ fill_indent(out, d2);
398
+ }
399
+ oj_dump_cstr(cols->str, cols->len, 0, 0, out);
400
+ *out->cur++ = ':';
401
+ dump_rails_val(rb_ary_entry(row, i), depth, out, true);
402
+ if (i < ccnt - 1) {
403
+ *out->cur++ = ',';
404
+ }
405
+ }
406
+ size = depth * out->indent + 1;
407
+ assure_size(out, size);
408
+ if (out->opts->dump_opts.use) {
409
+ if (0 < out->opts->dump_opts.array_size) {
410
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
411
+ out->cur += out->opts->dump_opts.array_size;
412
+ }
413
+ if (0 < out->opts->dump_opts.indent_size) {
414
+ int i;
415
+
416
+ for (i = depth; 0 < i; i--) {
417
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
418
+ out->cur += out->opts->dump_opts.indent_size;
419
+ }
420
+ }
421
+ } else {
422
+ fill_indent(out, depth);
423
+ }
424
+ *out->cur++ = '}';
425
+ }
426
+
427
+ static ID rows_id = 0;
428
+ static ID columns_id = 0;
429
+
430
+ static void
431
+ dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok) {
432
+ volatile VALUE rows;
433
+ StrLen cols;
434
+ int ccnt = 0;
435
+ int i, rcnt;
436
+ size_t size;
437
+ int d2 = depth + 1;
438
+
439
+ if (0 == rows_id) {
440
+ rows_id = rb_intern("@rows");
441
+ columns_id = rb_intern("@columns");
442
+ }
443
+ out->argc = 0;
444
+ cols = columns_array(rb_ivar_get(obj, columns_id), &ccnt);
445
+ rows = rb_ivar_get(obj, rows_id);
446
+ rcnt = (int)RARRAY_LEN(rows);
447
+ assure_size(out, 2);
448
+ *out->cur++ = '[';
449
+ if (out->opts->dump_opts.use) {
450
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
451
+ } else {
452
+ size = d2 * out->indent + 2;
453
+ }
454
+ assure_size(out, 2);
455
+ for (i = 0; i < rcnt; i++) {
456
+ assure_size(out, size);
457
+ if (out->opts->dump_opts.use) {
458
+ if (0 < out->opts->dump_opts.array_size) {
459
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
460
+ out->cur += out->opts->dump_opts.array_size;
461
+ }
462
+ if (0 < out->opts->dump_opts.indent_size) {
463
+ int i;
464
+ for (i = d2; 0 < i; i--) {
465
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
466
+ out->cur += out->opts->dump_opts.indent_size;
467
+ }
468
+ }
469
+ } else {
470
+ fill_indent(out, d2);
471
+ }
472
+ dump_row(rb_ary_entry(rows, i), cols, ccnt, d2, out);
473
+ if (i < rcnt - 1) {
474
+ *out->cur++ = ',';
475
+ }
476
+ }
477
+ xfree(cols);
478
+ size = depth * out->indent + 1;
479
+ assure_size(out, size);
480
+ if (out->opts->dump_opts.use) {
481
+ if (0 < out->opts->dump_opts.array_size) {
482
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
483
+ out->cur += out->opts->dump_opts.array_size;
484
+ }
485
+ if (0 < out->opts->dump_opts.indent_size) {
486
+ int i;
487
+
488
+ for (i = depth; 0 < i; i--) {
489
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
490
+ out->cur += out->opts->dump_opts.indent_size;
491
+ }
492
+ }
493
+ } else {
494
+ fill_indent(out, depth);
495
+ }
496
+ *out->cur++ = ']';
497
+ }
498
+
499
+ typedef struct _namedFunc {
500
+ const char *name;
501
+ DumpFunc func;
502
+ } *NamedFunc;
503
+
504
+ static void
505
+ dump_as_string(VALUE obj, int depth, Out out, bool as_ok) {
506
+ if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
507
+ out->argc = 0;
508
+ return;
509
+ }
510
+ oj_dump_obj_to_s(obj, out);
511
+ }
512
+
513
+ static void
514
+ dump_as_json(VALUE obj, int depth, Out out, bool as_ok) {
515
+ volatile VALUE ja;
516
+
517
+ if (Yes == out->opts->trace) {
518
+ oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
519
+ }
520
+ // Some classes elect to not take an options argument so check the arity
521
+ // of as_json.
522
+ if (0 == rb_obj_method_arity(obj, oj_as_json_id)) {
523
+ ja = rb_funcall(obj, oj_as_json_id, 0);
524
+ } else {
525
+ ja = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
526
+ }
527
+ if (Yes == out->opts->trace) {
528
+ oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
529
+ }
530
+
531
+ out->argc = 0;
532
+ if (ja == obj || !as_ok) {
533
+ // Once as_json is called it should never be called again on the same
534
+ // object with as_ok.
535
+ dump_rails_val(ja, depth, out, false);
536
+ } else {
537
+ int type = rb_type(ja);
538
+
539
+ if (T_HASH == type || T_ARRAY == type) {
540
+ dump_rails_val(ja, depth, out, true);
541
+ } else {
542
+ dump_rails_val(ja, depth, out, true);
543
+ }
544
+ }
545
+ }
546
+
547
+ static void
548
+ dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
549
+ if (as_ok && rb_respond_to(obj, oj_as_json_id)) {
550
+ dump_as_json(obj, depth, out, false);
551
+ return;
552
+ }
553
+ dump_as_string(obj, depth, out, as_ok);
554
+ }
555
+
556
+ static struct _namedFunc dump_map[] = {
557
+ { "ActionController::Parameters", dump_actioncontroller_parameters },
558
+ { "ActiveRecord::Result", dump_activerecord_result },
559
+ { "ActiveSupport::TimeWithZone", dump_timewithzone },
560
+ { "BigDecimal", dump_bigdecimal },
561
+ { "Range", dump_to_s },
562
+ { "Regexp", dump_regexp },
563
+ //{ "Regexp", dump_to_s },
564
+ { "Time", dump_time },
565
+ { NULL, NULL },
566
+ };
567
+
568
+ static VALUE activerecord_base = Qundef;
569
+ static ID attributes_id = 0;
570
+
571
+ static void
572
+ dump_activerecord(VALUE obj, int depth, Out out, bool as_ok) {
573
+ if (0 == attributes_id) {
574
+ attributes_id = rb_intern("@attributes");
575
+ }
576
+ out->argc = 0;
577
+ dump_rails_val(rb_ivar_get(obj, attributes_id), depth, out, true);
578
+ }
579
+
580
+ static ROpt
581
+ create_opt(ROptTable rot, VALUE clas) {
582
+ ROpt ro;
583
+ NamedFunc nf;
584
+ const char *classname = rb_class2name(clas);
585
+ int olen = rot->len;
586
+
587
+ rot->len++;
588
+ if (NULL == rot->table) {
589
+ rot->alen = 256;
590
+ rot->table = ALLOC_N(struct _rOpt, rot->alen);
591
+ memset(rot->table, 0, sizeof(struct _rOpt) * rot->alen);
592
+ } else if (rot->alen <= rot->len) {
593
+ rot->alen *= 2;
594
+ REALLOC_N(rot->table, struct _rOpt, rot->alen);
595
+ memset(rot->table + olen, 0, sizeof(struct _rOpt) * olen);
596
+ }
597
+ if (0 == olen) {
598
+ ro = rot->table;
599
+ } else if (rot->table[olen - 1].clas < clas) {
600
+ ro = &rot->table[olen];
601
+ } else {
602
+ int i;
603
+
604
+ for (i = 0, ro = rot->table; i < olen; i++, ro++) {
605
+ if (clas < ro->clas) {
606
+ memmove(ro + 1, ro, sizeof(struct _rOpt) * (olen - i));
607
+ break;
608
+ }
609
+ }
610
+ }
611
+ ro->clas = clas;
612
+ ro->on = true;
613
+ ro->dump = dump_obj_attrs;
614
+ for (nf = dump_map; NULL != nf->name; nf++) {
615
+ if (0 == strcmp(nf->name, classname)) {
616
+ ro->dump = nf->func;
617
+ break;
618
+ }
619
+ }
620
+ if (ro->dump == dump_obj_attrs) {
621
+ if (Qundef == activerecord_base) {
622
+ // If not defined let an exception be raised.
623
+ VALUE ar = rb_const_get_at(rb_cObject, rb_intern("ActiveRecord"));
624
+
625
+ if (Qundef != ar) {
626
+ activerecord_base = rb_const_get_at(ar, rb_intern("Base"));
627
+ }
628
+ }
629
+ if (Qundef != activerecord_base && Qtrue == rb_class_inherited_p(clas, activerecord_base)) {
630
+ ro->dump = dump_activerecord;
631
+ } else if (Qtrue == rb_class_inherited_p(clas, rb_cStruct)) { // check before enumerable
632
+ ro->dump = dump_struct;
633
+ } else if (Qtrue == rb_class_inherited_p(clas, rb_mEnumerable)) {
634
+ ro->dump = dump_enumerable;
635
+ } else if (Qtrue == rb_class_inherited_p(clas, rb_eException)) {
636
+ ro->dump = dump_to_s;
637
+ }
638
+ }
639
+ return ro;
640
+ }
641
+
642
+ static void
643
+ encoder_free(void *ptr) {
644
+ if (NULL != ptr) {
645
+ Encoder e = (Encoder)ptr;
646
+
647
+ if (NULL != e->ropts.table) {
648
+ xfree(e->ropts.table);
649
+ }
650
+ xfree(ptr);
651
+ }
652
+ }
653
+
654
+ static void
655
+ encoder_mark(void *ptr) {
656
+ if (NULL != ptr) {
657
+ Encoder e = (Encoder)ptr;
658
+
659
+ if (Qnil != e->arg) {
660
+ rb_gc_mark(e->arg);
661
+ }
662
+ }
663
+ }
664
+
665
+ /* Document-method: new
666
+ * call-seq: new(options=nil)
667
+ *
668
+ * Creates a new Encoder.
669
+ * - *options* [_Hash_] formatting options
670
+ */
671
+ static VALUE
672
+ encoder_new(int argc, VALUE *argv, VALUE self) {
673
+ Encoder e = ALLOC(struct _encoder);
674
+
675
+ e->opts = oj_default_options;
676
+ e->arg = Qnil;
677
+ copy_opts(&ropts, &e->ropts);
678
+
679
+ if (1 <= argc && Qnil != *argv) {
680
+ oj_parse_options(*argv, &e->opts);
681
+ e->arg = *argv;
682
+ }
683
+ return Data_Wrap_Struct(encoder_class, encoder_mark, encoder_free, e);
684
+ }
685
+
686
+ static VALUE
687
+ resolve_classpath(const char *name) {
688
+ char class_name[1024];
689
+ VALUE clas;
690
+ char *end = class_name + sizeof(class_name) - 1;
691
+ char *s;
692
+ const char *n = name;
693
+ ID cid;
694
+
695
+ clas = rb_cObject;
696
+ for (s = class_name; '\0' != *n; n++) {
697
+ if (':' == *n) {
698
+ *s = '\0';
699
+ n++;
700
+ if (':' != *n) {
701
+ return Qnil;
702
+ }
703
+ cid = rb_intern(class_name);
704
+ if (!rb_const_defined_at(clas, cid)) {
705
+ return Qnil;
706
+ }
707
+ clas = rb_const_get_at(clas, cid);
708
+ s = class_name;
709
+ } else if (end <= s) {
710
+ return Qnil;
711
+ } else {
712
+ *s++ = *n;
713
+ }
714
+ }
715
+ *s = '\0';
716
+ cid = rb_intern(class_name);
717
+ if (!rb_const_defined_at(clas, cid)) {
718
+ return Qnil;
719
+ }
720
+ clas = rb_const_get_at(clas, cid);
721
+
722
+ return clas;
723
+ }
724
+
725
+ static void
726
+ optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
727
+ ROpt ro;
728
+
729
+ if (0 == argc) {
730
+ int i;
731
+ NamedFunc nf;
732
+ VALUE clas;
733
+
734
+ oj_rails_hash_opt = on;
735
+ oj_rails_array_opt = on;
736
+ oj_rails_float_opt = on;
737
+
738
+ for (nf = dump_map; NULL != nf->name; nf++) {
739
+ if (Qnil != (clas = resolve_classpath(nf->name))) {
740
+ if (NULL == oj_rails_get_opt(rot, clas)) {
741
+ create_opt(rot, clas);
742
+ }
743
+ }
744
+ }
745
+ for (i = 0; i < rot->len; i++) {
746
+ rot->table[i].on = on;
747
+ }
748
+ }
749
+ for (; 0 < argc; argc--, argv++) {
750
+ if (rb_cHash == *argv) {
751
+ oj_rails_hash_opt = on;
752
+ } else if (rb_cArray == *argv) {
753
+ oj_rails_array_opt = on;
754
+ } else if (rb_cFloat == *argv) {
755
+ oj_rails_float_opt = on;
756
+ } else if (oj_string_writer_class == *argv) {
757
+ string_writer_optimized = on;
758
+ } else if (NULL != (ro = oj_rails_get_opt(rot, *argv)) ||
759
+ NULL != (ro = create_opt(rot, *argv))) {
760
+ ro->on = on;
761
+ }
762
+ }
763
+ }
764
+
765
+ /* Document-method optimize
766
+ * call-seq: optimize(*classes)
767
+ *
768
+ * Use Oj rails optimized routines to encode the specified classes. This
769
+ * ignores the as_json() method on the class and uses an internal encoding
770
+ * instead. Passing in no classes indicates all should use the optimized
771
+ * version of encoding for all previously optimized classes. Passing in the
772
+ * Object class set a global switch that will then use the optimized behavior
773
+ * for all classes.
774
+ *
775
+ * - *classes* [_Class_] a list of classes to optimize
776
+ */
777
+ static VALUE
778
+ encoder_optimize(int argc, VALUE *argv, VALUE self) {
779
+ Encoder e = (Encoder)DATA_PTR(self);
780
+
781
+ optimize(argc, argv, &e->ropts, true);
782
+
783
+ return Qnil;
784
+ }
785
+
786
+ /* Document-method: optimize
787
+ * call-seq: optimize(*classes)
788
+ *
789
+ * Use Oj rails optimized routines to encode the specified classes. This
790
+ * ignores the as_json() method on the class and uses an internal encoding
791
+ * instead. Passing in no classes indicates all should use the optimized
792
+ * version of encoding for all previously optimized classes. Passing in the
793
+ * Object class set a global switch that will then use the optimized behavior
794
+ * for all classes.
795
+ *
796
+ * - *classes* [_Class_] a list of classes to optimize
797
+ */
798
+ static VALUE
799
+ rails_optimize(int argc, VALUE *argv, VALUE self) {
800
+ optimize(argc, argv, &ropts, true);
801
+ string_writer_optimized = true;
802
+
803
+ return Qnil;
804
+ }
805
+
806
+ /* Document-module: mimic_JSON
807
+ * call-seq: mimic_JSON()
808
+ *
809
+ * Sets the JSON method to use Oj similar to Oj.mimic_JSON except with the
810
+ * ActiveSupport monkey patches instead of the json gem monkey patches.
811
+ */
812
+ VALUE
813
+ rails_mimic_json(VALUE self) {
814
+ VALUE json;
815
+
816
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
817
+ json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
818
+ } else {
819
+ json = rb_define_module("JSON");
820
+ }
821
+ oj_mimic_json_methods(json);
822
+ //oj_default_options.mode = RailsMode;
823
+
824
+ return Qnil;
825
+ }
826
+
827
+ /* Document-method: deoptimize
828
+ * call-seq: deoptimize(*classes)
829
+ *
830
+ * Turn off Oj rails optimization on the specified classes.
831
+ *
832
+ * - *classes* [_Class_] a list of classes to deoptimize
833
+ */
834
+ static VALUE
835
+ encoder_deoptimize(int argc, VALUE *argv, VALUE self) {
836
+ Encoder e = (Encoder)DATA_PTR(self);
837
+
838
+ optimize(argc, argv, &e->ropts, false);
839
+
840
+ return Qnil;
841
+ }
842
+
843
+ /* Document-method: deoptimize
844
+ * call-seq: deoptimize(*classes)
845
+ *
846
+ * Turn off Oj rails optimization on the specified classes.
847
+ *
848
+ * - *classes* [_Class_] a list of classes to deoptimize
849
+ */
850
+ static VALUE
851
+ rails_deoptimize(int argc, VALUE *argv, VALUE self) {
852
+ optimize(argc, argv, &ropts, false);
853
+ string_writer_optimized = false;
854
+
855
+ return Qnil;
856
+ }
857
+
858
+ /* Document-method:optimized?
859
+ * call-seq: optimized?(clas)
860
+ *
861
+ * - *clas* [_Class_] Class to check
862
+ *
863
+ * @return true if the class is being optimized for rails and false otherwise
864
+ */
865
+ static VALUE
866
+ encoder_optimized(VALUE self, VALUE clas) {
867
+ Encoder e = (Encoder)DATA_PTR(self);
868
+ ROpt ro = oj_rails_get_opt(&e->ropts, clas);
869
+
870
+ if (NULL == ro) {
871
+ return Qfalse;
872
+ }
873
+ return (ro->on) ? Qtrue : Qfalse;
874
+ }
875
+
876
+ /* Document-method: optimized?
877
+ * call-seq: optimized?(clas)
878
+ *
879
+ * Returns true if the specified Class is being optimized.
880
+ */
881
+ static VALUE
882
+ rails_optimized(VALUE self, VALUE clas) {
883
+ ROpt ro = oj_rails_get_opt(&ropts, clas);
884
+
885
+ if (NULL == ro) {
886
+ return Qfalse;
887
+ }
888
+ return (ro->on) ? Qtrue : Qfalse;
889
+ }
890
+
891
+ typedef struct _oo {
892
+ Out out;
893
+ VALUE obj;
894
+ } *OO;
895
+
896
+ static VALUE
897
+ protect_dump(VALUE ov) {
898
+ OO oo = (OO)ov;
899
+
900
+ dump_rails_val(oo->obj, 0, oo->out, true);
901
+
902
+ return Qnil;
903
+ }
904
+
905
+ static VALUE
906
+ encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *argv) {
907
+ char buf[4096];
908
+ struct _out out;
909
+ struct _options copts = *opts;
910
+ volatile VALUE rstr = Qnil;
911
+ struct _oo oo;
912
+ int line = 0;
913
+
914
+ oo.out = &out;
915
+ oo.obj = obj;
916
+ copts.str_rx.head = NULL;
917
+ copts.str_rx.tail = NULL;
918
+ copts.mode = RailsMode;
919
+ if (escape_html) {
920
+ copts.escape_mode = RailsXEsc;
921
+ } else {
922
+ copts.escape_mode = RailsEsc;
923
+ }
924
+ out.buf = buf;
925
+ out.end = buf + sizeof(buf) - 10;
926
+ out.allocated = false;
927
+ out.omit_nil = copts.dump_opts.omit_nil;
928
+ out.caller = 0;
929
+ out.cur = out.buf;
930
+ out.circ_cnt = 0;
931
+ out.opts = &copts;
932
+ out.hash_cnt = 0;
933
+ out.indent = copts.indent;
934
+ out.argc = argc;
935
+ out.argv = argv;
936
+ out.ropts = ropts;
937
+ if (Yes == copts.circular) {
938
+ oj_cache8_new(&out.circ_cache);
939
+ }
940
+ //dump_rails_val(*argv, 0, &out, true);
941
+ rb_protect(protect_dump, (VALUE)&oo, &line);
942
+
943
+ if (0 == line) {
944
+ if (0 < out.indent) {
945
+ switch (*(out.cur - 1)) {
946
+ case ']':
947
+ case '}':
948
+ assure_size(&out, 2);
949
+ *out.cur++ = '\n';
950
+ default:
951
+ break;
952
+ }
953
+ }
954
+ *out.cur = '\0';
955
+
956
+ if (0 == out.buf) {
957
+ rb_raise(rb_eNoMemError, "Not enough memory.");
958
+ }
959
+ rstr = rb_str_new2(out.buf);
960
+ rstr = oj_encode(rstr);
961
+ }
962
+ if (Yes == copts.circular) {
963
+ oj_cache8_delete(out.circ_cache);
964
+ }
965
+ if (out.allocated) {
966
+ xfree(out.buf);
967
+ }
968
+ if (0 != line) {
969
+ rb_jump_tag(line);
970
+ }
971
+ return rstr;
972
+ }
973
+
974
+ /* Document-method: encode
975
+ * call-seq: encode(obj)
976
+ *
977
+ * - *obj* [_Object_] object to encode
978
+ *
979
+ * Returns encoded object as a JSON string.
980
+ */
981
+ static VALUE
982
+ encoder_encode(VALUE self, VALUE obj) {
983
+ Encoder e = (Encoder)DATA_PTR(self);
984
+
985
+ if (Qnil != e->arg) {
986
+ VALUE argv[1] = { e->arg };
987
+
988
+ return encode(obj, &e->ropts, &e->opts, 1, argv);
989
+ }
990
+ return encode(obj, &e->ropts, &e->opts, 0, NULL);
991
+ }
992
+
993
+ /* Document-method: encode
994
+ * call-seq: encode(obj, opts=nil)
995
+ *
996
+ * Encode obj as a JSON String.
997
+ *
998
+ * - *obj* [_Object_|Hash|Array] object to convert to a JSON String
999
+ * - *opts* [_Hash_] options
1000
+ *
1001
+ * Returns [_String_]
1002
+ */
1003
+ static VALUE
1004
+ rails_encode(int argc, VALUE *argv, VALUE self) {
1005
+ if (1 > argc) {
1006
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
1007
+ }
1008
+ if (1 == argc) {
1009
+ return encode(*argv, NULL, &oj_default_options, 0, NULL);
1010
+ } else {
1011
+ return encode(*argv, NULL, &oj_default_options, argc - 1, argv + 1);
1012
+ }
1013
+ }
1014
+
1015
+ static VALUE
1016
+ rails_use_standard_json_time_format(VALUE self, VALUE state) {
1017
+ if (Qtrue == state || Qfalse == state) {
1018
+ // no change needed
1019
+ } else if (Qnil == state) {
1020
+ state = Qfalse;
1021
+ } else {
1022
+ state = Qtrue;
1023
+ }
1024
+ rb_iv_set(self, "@use_standard_json_time_format", state);
1025
+ xml_time = Qtrue == state;
1026
+
1027
+ return state;
1028
+ }
1029
+
1030
+ static VALUE
1031
+ rails_use_standard_json_time_format_get(VALUE self) {
1032
+ return xml_time ? Qtrue : Qfalse;
1033
+ }
1034
+
1035
+ static VALUE
1036
+ rails_escape_html_entities_in_json(VALUE self, VALUE state) {
1037
+ rb_iv_set(self, "@escape_html_entities_in_json", state);
1038
+ escape_html = Qtrue == state;
1039
+
1040
+ return state;
1041
+ }
1042
+
1043
+ static VALUE
1044
+ rails_escape_html_entities_in_json_get(VALUE self) {
1045
+ return escape_html ? Qtrue : Qfalse;
1046
+ }
1047
+
1048
+ static VALUE
1049
+ rails_time_precision(VALUE self, VALUE prec) {
1050
+ rb_iv_set(self, "@time_precision", prec);
1051
+ oj_default_options.sec_prec = NUM2INT(prec);
1052
+ oj_default_options.sec_prec_set = true;
1053
+
1054
+ return prec;
1055
+ }
1056
+
1057
+ /* Document-method: set_encoder
1058
+ * call-seq: set_encoder()
1059
+ *
1060
+ * Sets the ActiveSupport.encoder to Oj::Rails::Encoder and wraps some of the
1061
+ * formatting globals used by ActiveSupport to allow the use of those globals
1062
+ * in the Oj::Rails optimizations.
1063
+ */
1064
+ static VALUE
1065
+ rails_set_encoder(VALUE self) {
1066
+ VALUE active;
1067
+ VALUE json;
1068
+ VALUE encoding;
1069
+ VALUE pv;
1070
+ VALUE verbose;
1071
+ VALUE enc = resolve_classpath("ActiveSupport::JSON::Encoding");
1072
+
1073
+ if (Qnil != enc) {
1074
+ escape_html = Qtrue == rb_iv_get(self, "@escape_html_entities_in_json");
1075
+ xml_time = Qtrue == rb_iv_get(enc, "@use_standard_json_time_format");
1076
+ }
1077
+ if (rb_const_defined_at(rb_cObject, rb_intern("ActiveSupport"))) {
1078
+ active = rb_const_get_at(rb_cObject, rb_intern("ActiveSupport"));
1079
+ } else {
1080
+ rb_raise(rb_eStandardError, "ActiveSupport not loaded.");
1081
+ }
1082
+ rb_funcall(active, rb_intern("json_encoder="), 1, encoder_class);
1083
+
1084
+ json = rb_const_get_at(active, rb_intern("JSON"));
1085
+ encoding = rb_const_get_at(json, rb_intern("Encoding"));
1086
+
1087
+ // rb_undef_method doesn't work for modules or maybe sometimes
1088
+ // doesn't. Anyway setting verbose should hide the warning.
1089
+ verbose = rb_gv_get("$VERBOSE");
1090
+ rb_gv_set("$VERBOSE", Qfalse);
1091
+ rb_undef_method(encoding, "use_standard_json_time_format=");
1092
+ rb_define_module_function(encoding, "use_standard_json_time_format=", rails_use_standard_json_time_format, 1);
1093
+ rb_undef_method(encoding, "use_standard_json_time_format");
1094
+ rb_define_module_function(encoding, "use_standard_json_time_format", rails_use_standard_json_time_format_get, 0);
1095
+
1096
+ pv = rb_iv_get(encoding, "@escape_html_entities_in_json");
1097
+ escape_html = Qtrue == pv;
1098
+ rb_undef_method(encoding, "escape_html_entities_in_json=");
1099
+ rb_define_module_function(encoding, "escape_html_entities_in_json=", rails_escape_html_entities_in_json, 1);
1100
+ rb_undef_method(encoding, "escape_html_entities_in_json");
1101
+ rb_define_module_function(encoding, "escape_html_entities_in_json", rails_escape_html_entities_in_json_get, 0);
1102
+
1103
+ pv = rb_iv_get(encoding, "@time_precision");
1104
+ oj_default_options.sec_prec = NUM2INT(pv);
1105
+ oj_default_options.sec_prec_set = true;
1106
+ rb_undef_method(encoding, "time_precision=");
1107
+ rb_define_module_function(encoding, "time_precision=", rails_time_precision, 1);
1108
+ rb_gv_set("$VERBOSE", verbose);
1109
+
1110
+ return Qnil;
1111
+ }
1112
+
1113
+ /* Document-method: set_decoder
1114
+ * call-seq: set_decoder()
1115
+ *
1116
+ * Sets the JSON.parse function to be the Oj::parse function which is json gem
1117
+ * compatible.
1118
+ */
1119
+ static VALUE
1120
+ rails_set_decoder(VALUE self) {
1121
+ VALUE json;
1122
+ VALUE json_error;
1123
+ VALUE verbose;
1124
+
1125
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
1126
+ json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
1127
+ } else {
1128
+ json = rb_define_module("JSON");
1129
+ }
1130
+ if (rb_const_defined_at(json, rb_intern("JSONError"))) {
1131
+ json_error = rb_const_get(json, rb_intern("JSONError"));
1132
+ } else {
1133
+ json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
1134
+ }
1135
+ if (rb_const_defined_at(json, rb_intern("ParserError"))) {
1136
+ oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
1137
+ } else {
1138
+ oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
1139
+ }
1140
+ // rb_undef_method doesn't work for modules or maybe sometimes
1141
+ // doesn't. Anyway setting verbose should hide the warning.
1142
+ verbose = rb_gv_get("$VERBOSE");
1143
+ rb_gv_set("$VERBOSE", Qfalse);
1144
+ rb_undef_method(json, "parse");
1145
+ rb_define_module_function(json, "parse", oj_mimic_parse, -1);
1146
+ rb_gv_set("$VERBOSE", verbose);
1147
+
1148
+ return Qnil;
1149
+ }
1150
+
1151
+ /* Document-module: Oj.optimize_rails()
1152
+ *
1153
+ * Sets the Oj as the Rails encoder and decoder. Oj::Rails.optimize is also
1154
+ * called.
1155
+ */
1156
+ VALUE
1157
+ oj_optimize_rails(VALUE self) {
1158
+ rails_set_encoder(self);
1159
+ rails_set_decoder(self);
1160
+ rails_optimize(0, NULL, self);
1161
+ rails_mimic_json(self);
1162
+
1163
+ return Qnil;
1164
+ }
1165
+
1166
+ /* Document-module: Oj::Rails
1167
+ *
1168
+ * Module that provides rails and active support compatibility.
1169
+ */
1170
+ /* Document-class: Oj::Rails::Encoder
1171
+ *
1172
+ * The Oj ActiveSupport compliant encoder.
1173
+ */
1174
+ void
1175
+ oj_mimic_rails_init() {
1176
+ VALUE rails = rb_define_module_under(Oj, "Rails");
1177
+
1178
+ rb_define_module_function(rails, "encode", rails_encode, -1);
1179
+
1180
+ encoder_class = rb_define_class_under(rails, "Encoder", rb_cObject);
1181
+ rb_define_module_function(encoder_class, "new", encoder_new, -1);
1182
+ rb_define_module_function(rails, "optimize", rails_optimize, -1);
1183
+ rb_define_module_function(rails, "deoptimize", rails_deoptimize, -1);
1184
+ rb_define_module_function(rails, "optimized?", rails_optimized, 1);
1185
+ rb_define_module_function(rails, "mimic_JSON", rails_mimic_json, 0);
1186
+
1187
+ rb_define_module_function(rails, "set_encoder", rails_set_encoder, 0);
1188
+ rb_define_module_function(rails, "set_decoder", rails_set_decoder, 0);
1189
+
1190
+ rb_define_method(encoder_class, "encode", encoder_encode, 1);
1191
+ rb_define_method(encoder_class, "optimize", encoder_optimize, -1);
1192
+ rb_define_method(encoder_class, "deoptimize", encoder_deoptimize, -1);
1193
+ rb_define_method(encoder_class, "optimized?", encoder_optimized, 1);
1194
+ }
1195
+
1196
+ static void
1197
+ dump_to_hash(VALUE obj, int depth, Out out) {
1198
+ dump_rails_val(rb_funcall(obj, oj_to_hash_id, 0), depth, out, true);
1199
+ }
1200
+
1201
+ static void
1202
+ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1203
+ char buf[64];
1204
+ char *b;
1205
+ double d = rb_num2dbl(obj);
1206
+ int cnt = 0;
1207
+
1208
+ if (0.0 == d) {
1209
+ b = buf;
1210
+ *b++ = '0';
1211
+ *b++ = '.';
1212
+ *b++ = '0';
1213
+ *b++ = '\0';
1214
+ cnt = 3;
1215
+ } else {
1216
+ if (isnan(d) || OJ_INFINITY == d || -OJ_INFINITY == d) {
1217
+ strcpy(buf, "null");
1218
+ cnt = 4;
1219
+ } else if (d == (double)(long long int)d) {
1220
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1221
+ } else if (oj_rails_float_opt) {
1222
+ cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, "%0.16g");
1223
+ } else {
1224
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1225
+
1226
+ strcpy(buf, rb_string_value_ptr((VALUE*)&rstr));
1227
+ cnt = (int)RSTRING_LEN(rstr);
1228
+ }
1229
+ }
1230
+ assure_size(out, cnt);
1231
+ for (b = buf; '\0' != *b; b++) {
1232
+ *out->cur++ = *b;
1233
+ }
1234
+ *out->cur = '\0';
1235
+ }
1236
+
1237
+ static void
1238
+ dump_array(VALUE a, int depth, Out out, bool as_ok) {
1239
+ size_t size;
1240
+ int i, cnt;
1241
+ int d2 = depth + 1;
1242
+
1243
+ if (Yes == out->opts->circular) {
1244
+ if (0 > oj_check_circular(a, out)) {
1245
+ oj_dump_nil(Qnil, 0, out, false);
1246
+ return;
1247
+ }
1248
+ }
1249
+ //if (!oj_rails_array_opt && as_ok && 0 < out->argc && rb_respond_to(a, oj_as_json_id)) {
1250
+ if (as_ok && 0 < out->argc && rb_respond_to(a, oj_as_json_id)) {
1251
+ dump_as_json(a, depth, out, false);
1252
+ return;
1253
+ }
1254
+ cnt = (int)RARRAY_LEN(a);
1255
+ *out->cur++ = '[';
1256
+ size = 2;
1257
+ assure_size(out, size);
1258
+ if (0 == cnt) {
1259
+ *out->cur++ = ']';
1260
+ } else {
1261
+ if (out->opts->dump_opts.use) {
1262
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
1263
+ } else {
1264
+ size = d2 * out->indent + 2;
1265
+ }
1266
+ cnt--;
1267
+ for (i = 0; i <= cnt; i++) {
1268
+ assure_size(out, size);
1269
+ if (out->opts->dump_opts.use) {
1270
+ if (0 < out->opts->dump_opts.array_size) {
1271
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
1272
+ out->cur += out->opts->dump_opts.array_size;
1273
+ }
1274
+ if (0 < out->opts->dump_opts.indent_size) {
1275
+ int i;
1276
+ for (i = d2; 0 < i; i--) {
1277
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1278
+ out->cur += out->opts->dump_opts.indent_size;
1279
+ }
1280
+ }
1281
+ } else {
1282
+ fill_indent(out, d2);
1283
+ }
1284
+ dump_rails_val(rb_ary_entry(a, i), d2, out, true);
1285
+ if (i < cnt) {
1286
+ *out->cur++ = ',';
1287
+ }
1288
+ }
1289
+ size = depth * out->indent + 1;
1290
+ assure_size(out, size);
1291
+ if (out->opts->dump_opts.use) {
1292
+ if (0 < out->opts->dump_opts.array_size) {
1293
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
1294
+ out->cur += out->opts->dump_opts.array_size;
1295
+ }
1296
+ if (0 < out->opts->dump_opts.indent_size) {
1297
+ int i;
1298
+
1299
+ for (i = depth; 0 < i; i--) {
1300
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1301
+ out->cur += out->opts->dump_opts.indent_size;
1302
+ }
1303
+ }
1304
+ } else {
1305
+ fill_indent(out, depth);
1306
+ }
1307
+ *out->cur++ = ']';
1308
+ }
1309
+ *out->cur = '\0';
1310
+ }
1311
+
1312
+ static int
1313
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
1314
+ Out out = (Out)ov;
1315
+ int depth = out->depth;
1316
+ long size;
1317
+ int rtype = rb_type(key);
1318
+
1319
+ if (out->omit_nil && Qnil == value) {
1320
+ return ST_CONTINUE;
1321
+ }
1322
+ if (rtype != T_STRING && rtype != T_SYMBOL) {
1323
+ key = rb_funcall(key, oj_to_s_id, 0);
1324
+ rtype = rb_type(key);
1325
+ }
1326
+ if (!out->opts->dump_opts.use) {
1327
+ size = depth * out->indent + 1;
1328
+ assure_size(out, size);
1329
+ fill_indent(out, depth);
1330
+ if (rtype == T_STRING) {
1331
+ oj_dump_str(key, 0, out, false);
1332
+ } else {
1333
+ oj_dump_sym(key, 0, out, false);
1334
+ }
1335
+ *out->cur++ = ':';
1336
+ } else {
1337
+ size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
1338
+ assure_size(out, size);
1339
+ if (0 < out->opts->dump_opts.hash_size) {
1340
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
1341
+ out->cur += out->opts->dump_opts.hash_size;
1342
+ }
1343
+ if (0 < out->opts->dump_opts.indent_size) {
1344
+ int i;
1345
+ for (i = depth; 0 < i; i--) {
1346
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1347
+ out->cur += out->opts->dump_opts.indent_size;
1348
+ }
1349
+ }
1350
+ if (rtype == T_STRING) {
1351
+ oj_dump_str(key, 0, out, false);
1352
+ } else {
1353
+ oj_dump_sym(key, 0, out, false);
1354
+ }
1355
+ size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
1356
+ assure_size(out, size);
1357
+ if (0 < out->opts->dump_opts.before_size) {
1358
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
1359
+ out->cur += out->opts->dump_opts.before_size;
1360
+ }
1361
+ *out->cur++ = ':';
1362
+ if (0 < out->opts->dump_opts.after_size) {
1363
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
1364
+ out->cur += out->opts->dump_opts.after_size;
1365
+ }
1366
+ }
1367
+ dump_rails_val(value, depth, out, true);
1368
+ out->depth = depth;
1369
+ *out->cur++ = ',';
1370
+
1371
+ return ST_CONTINUE;
1372
+ }
1373
+
1374
+ static void
1375
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
1376
+ int cnt;
1377
+ size_t size;
1378
+
1379
+ if (Yes == out->opts->circular) {
1380
+ if (0 > oj_check_circular(obj, out)) {
1381
+ oj_dump_nil(Qnil, 0, out, false);
1382
+ return;
1383
+ }
1384
+ }
1385
+ if ((!oj_rails_hash_opt || 0 < out->argc) && as_ok && rb_respond_to(obj, oj_as_json_id)) {
1386
+ dump_as_json(obj, depth, out, false);
1387
+ return;
1388
+ }
1389
+ cnt = (int)RHASH_SIZE(obj);
1390
+ size = depth * out->indent + 2;
1391
+ assure_size(out, 2);
1392
+ *out->cur++ = '{';
1393
+ if (0 == cnt) {
1394
+ *out->cur++ = '}';
1395
+ } else {
1396
+ out->depth = depth + 1;
1397
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
1398
+ if (',' == *(out->cur - 1)) {
1399
+ out->cur--; // backup to overwrite last comma
1400
+ }
1401
+ if (!out->opts->dump_opts.use) {
1402
+ assure_size(out, size);
1403
+ fill_indent(out, depth);
1404
+ } else {
1405
+ size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
1406
+ assure_size(out, size);
1407
+ if (0 < out->opts->dump_opts.hash_size) {
1408
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
1409
+ out->cur += out->opts->dump_opts.hash_size;
1410
+ }
1411
+ if (0 < out->opts->dump_opts.indent_size) {
1412
+ int i;
1413
+
1414
+ for (i = depth; 0 < i; i--) {
1415
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1416
+ out->cur += out->opts->dump_opts.indent_size;
1417
+ }
1418
+ }
1419
+ }
1420
+ *out->cur++ = '}';
1421
+ }
1422
+ *out->cur = '\0';
1423
+ }
1424
+
1425
+ static void
1426
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
1427
+ VALUE clas;
1428
+
1429
+ if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
1430
+ out->argc = 0;
1431
+ return;
1432
+ }
1433
+ clas = rb_obj_class(obj);
1434
+ if (as_ok) {
1435
+ ROpt ro;
1436
+
1437
+ if (NULL != (ro = oj_rails_get_opt(out->ropts, clas)) && ro->on) {
1438
+ ro->dump(obj, depth, out, as_ok);
1439
+ } else if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
1440
+ oj_dump_raw_json(obj, depth, out);
1441
+ } else if (rb_respond_to(obj, oj_as_json_id)) {
1442
+ dump_as_json(obj, depth, out, true);
1443
+ } else if (rb_respond_to(obj, oj_to_hash_id)) {
1444
+ dump_to_hash(obj, depth, out);
1445
+ } else if (oj_bigdecimal_class == clas) {
1446
+ dump_bigdecimal(obj, depth, out, false);
1447
+ } else {
1448
+ oj_dump_obj_to_s(obj, out);
1449
+ }
1450
+ } else if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
1451
+ oj_dump_raw_json(obj, depth, out);
1452
+ } else if (rb_respond_to(obj, oj_to_hash_id)) {
1453
+ // Always attempt to_hash.
1454
+ dump_to_hash(obj, depth, out);
1455
+ } else if (oj_bigdecimal_class == clas) {
1456
+ dump_bigdecimal(obj, depth, out, false);
1457
+ } else {
1458
+ oj_dump_obj_to_s(obj, out);
1459
+ }
1460
+ }
1461
+
1462
+ static DumpFunc rails_funcs[] = {
1463
+ NULL, // RUBY_T_NONE = 0x00,
1464
+ dump_obj, // RUBY_T_OBJECT = 0x01,
1465
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
1466
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
1467
+ dump_float, // RUBY_T_FLOAT = 0x04,
1468
+ oj_dump_str, // RUBY_T_STRING = 0x05,
1469
+ dump_regexp, // RUBY_T_REGEXP = 0x06,
1470
+ //dump_as_string, // RUBY_T_REGEXP = 0x06,
1471
+ dump_array, // RUBY_T_ARRAY = 0x07,
1472
+ dump_hash, // RUBY_T_HASH = 0x08,
1473
+ dump_obj, // RUBY_T_STRUCT = 0x09,
1474
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
1475
+ dump_as_string, // RUBY_T_FILE = 0x0b,
1476
+ dump_obj, // RUBY_T_DATA = 0x0c,
1477
+ NULL, // RUBY_T_MATCH = 0x0d,
1478
+ // Rails raises a stack error on Complex and Rational. It also corrupts
1479
+ // something which causes a segfault on the next call. Oj will not mimic
1480
+ // that behavior.
1481
+ dump_as_string, // RUBY_T_COMPLEX = 0x0e,
1482
+ dump_as_string, // RUBY_T_RATIONAL = 0x0f,
1483
+ NULL, // 0x10
1484
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
1485
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
1486
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
1487
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
1488
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
1489
+ };
1490
+
1491
+ static void
1492
+ dump_rails_val(VALUE obj, int depth, Out out, bool as_ok) {
1493
+ int type = rb_type(obj);
1494
+
1495
+ if (Yes == out->opts->trace) {
1496
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
1497
+ }
1498
+ if (MAX_DEPTH < depth) {
1499
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
1500
+ }
1501
+ if (0 < type && type <= RUBY_T_FIXNUM) {
1502
+ DumpFunc f = rails_funcs[type];
1503
+
1504
+ if (NULL != f) {
1505
+ f(obj, depth, out, as_ok);
1506
+ if (Yes == out->opts->trace) {
1507
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
1508
+ }
1509
+ return;
1510
+ }
1511
+ }
1512
+ oj_dump_nil(Qnil, depth, out, false);
1513
+ if (Yes == out->opts->trace) {
1514
+ oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
1515
+ }
1516
+ }
1517
+
1518
+ void
1519
+ oj_dump_rails_val(VALUE obj, int depth, Out out) {
1520
+ out->opts->str_rx.head = NULL;
1521
+ out->opts->str_rx.tail = NULL;
1522
+ if (escape_html) {
1523
+ out->opts->escape_mode = RailsXEsc;
1524
+ } else {
1525
+ out->opts->escape_mode = RailsEsc;
1526
+ }
1527
+ dump_rails_val(obj, depth, out, true);
1528
+ }