oj 3.8.0

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