oj 3.7.12

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 +96 -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 +1188 -0
  13. data/ext/oj/dump.c +1232 -0
  14. data/ext/oj/dump.h +94 -0
  15. data/ext/oj/dump_compat.c +973 -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 +873 -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 +1694 -0
  32. data/ext/oj/oj.h +381 -0
  33. data/ext/oj/parse.c +1085 -0
  34. data/ext/oj/parse.h +111 -0
  35. data/ext/oj/rails.c +1485 -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 +512 -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 +154 -0
  73. data/pages/Options.md +266 -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/big.rb +15 -0
  90. data/test/files.rb +29 -0
  91. data/test/foo.rb +33 -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/mem.rb +35 -0
  113. data/test/perf.rb +107 -0
  114. data/test/perf_compat.rb +130 -0
  115. data/test/perf_fast.rb +164 -0
  116. data/test/perf_file.rb +64 -0
  117. data/test/perf_object.rb +138 -0
  118. data/test/perf_saj.rb +109 -0
  119. data/test/perf_scp.rb +151 -0
  120. data/test/perf_simple.rb +287 -0
  121. data/test/perf_strict.rb +145 -0
  122. data/test/perf_wab.rb +131 -0
  123. data/test/sample.rb +54 -0
  124. data/test/sample/change.rb +14 -0
  125. data/test/sample/dir.rb +19 -0
  126. data/test/sample/doc.rb +36 -0
  127. data/test/sample/file.rb +48 -0
  128. data/test/sample/group.rb +16 -0
  129. data/test/sample/hasprops.rb +16 -0
  130. data/test/sample/layer.rb +12 -0
  131. data/test/sample/line.rb +20 -0
  132. data/test/sample/oval.rb +10 -0
  133. data/test/sample/rect.rb +10 -0
  134. data/test/sample/shape.rb +35 -0
  135. data/test/sample/text.rb +20 -0
  136. data/test/sample_json.rb +37 -0
  137. data/test/test_compat.rb +509 -0
  138. data/test/test_custom.rb +406 -0
  139. data/test/test_debian.rb +53 -0
  140. data/test/test_fast.rb +470 -0
  141. data/test/test_file.rb +239 -0
  142. data/test/test_gc.rb +49 -0
  143. data/test/test_hash.rb +29 -0
  144. data/test/test_integer_range.rb +73 -0
  145. data/test/test_null.rb +376 -0
  146. data/test/test_object.rb +1018 -0
  147. data/test/test_saj.rb +186 -0
  148. data/test/test_scp.rb +433 -0
  149. data/test/test_strict.rb +410 -0
  150. data/test/test_various.rb +739 -0
  151. data/test/test_wab.rb +307 -0
  152. data/test/test_writer.rb +380 -0
  153. data/test/tests.rb +24 -0
  154. data/test/tests_mimic.rb +14 -0
  155. data/test/tests_mimic_addition.rb +7 -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,1485 @@
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 (NULL != (ro = oj_rails_get_opt(rot, *argv)) ||
754
+ NULL != (ro = create_opt(rot, *argv))) {
755
+ ro->on = on;
756
+ }
757
+ }
758
+ }
759
+
760
+ /* Document-method optimize
761
+ * call-seq: optimize(*classes)
762
+ *
763
+ * Use Oj rails optimized routines to encode the specified classes. This
764
+ * ignores the as_json() method on the class and uses an internal encoding
765
+ * instead. Passing in no classes indicates all should use the optimized
766
+ * version of encoding for all previously optimized classes. Passing in the
767
+ * Object class set a global switch that will then use the optimized behavior
768
+ * for all classes.
769
+ *
770
+ * - *classes* [_Class_] a list of classes to optimize
771
+ */
772
+ static VALUE
773
+ encoder_optimize(int argc, VALUE *argv, VALUE self) {
774
+ Encoder e = (Encoder)DATA_PTR(self);
775
+
776
+ optimize(argc, argv, &e->ropts, true);
777
+
778
+ return Qnil;
779
+ }
780
+
781
+ /* Document-method: optimize
782
+ * call-seq: optimize(*classes)
783
+ *
784
+ * Use Oj rails optimized routines to encode the specified classes. This
785
+ * ignores the as_json() method on the class and uses an internal encoding
786
+ * instead. Passing in no classes indicates all should use the optimized
787
+ * version of encoding for all previously optimized classes. Passing in the
788
+ * Object class set a global switch that will then use the optimized behavior
789
+ * for all classes.
790
+ *
791
+ * - *classes* [_Class_] a list of classes to optimize
792
+ */
793
+ static VALUE
794
+ rails_optimize(int argc, VALUE *argv, VALUE self) {
795
+ optimize(argc, argv, &ropts, true);
796
+
797
+ return Qnil;
798
+ }
799
+
800
+ /* Document-module: mimic_JSON
801
+ * call-seq: mimic_JSON()
802
+ *
803
+ * Sets the JSON method to use Oj similar to Oj.mimic_JSON except with the
804
+ * ActiveSupport monkey patches instead of the json gem monkey patches.
805
+ */
806
+ VALUE
807
+ rails_mimic_json(VALUE self) {
808
+ VALUE json;
809
+
810
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
811
+ json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
812
+ } else {
813
+ json = rb_define_module("JSON");
814
+ }
815
+ oj_mimic_json_methods(json);
816
+ //oj_default_options.mode = RailsMode;
817
+
818
+ return Qnil;
819
+ }
820
+
821
+ /* Document-method: deoptimize
822
+ * call-seq: deoptimize(*classes)
823
+ *
824
+ * Turn off Oj rails optimization on the specified classes.
825
+ *
826
+ * - *classes* [_Class_] a list of classes to deoptimize
827
+ */
828
+ static VALUE
829
+ encoder_deoptimize(int argc, VALUE *argv, VALUE self) {
830
+ Encoder e = (Encoder)DATA_PTR(self);
831
+
832
+ optimize(argc, argv, &e->ropts, false);
833
+
834
+ return Qnil;
835
+ }
836
+
837
+ /* Document-method: deoptimize
838
+ * call-seq: deoptimize(*classes)
839
+ *
840
+ * Turn off Oj rails optimization on the specified classes.
841
+ *
842
+ * - *classes* [_Class_] a list of classes to deoptimize
843
+ */
844
+ static VALUE
845
+ rails_deoptimize(int argc, VALUE *argv, VALUE self) {
846
+ optimize(argc, argv, &ropts, false);
847
+
848
+ return Qnil;
849
+ }
850
+
851
+ /* Document-method:optimized?
852
+ * call-seq: optimized?(clas)
853
+ *
854
+ * - *clas* [_Class_] Class to check
855
+ *
856
+ * @return true if the class is being optimized for rails and false otherwise
857
+ */
858
+ static VALUE
859
+ encoder_optimized(VALUE self, VALUE clas) {
860
+ Encoder e = (Encoder)DATA_PTR(self);
861
+ ROpt ro = oj_rails_get_opt(&e->ropts, clas);
862
+
863
+ if (NULL == ro) {
864
+ return Qfalse;
865
+ }
866
+ return (ro->on) ? Qtrue : Qfalse;
867
+ }
868
+
869
+ /* Document-method: optimized?
870
+ * call-seq: optimized?(clas)
871
+ *
872
+ * Returns true if the specified Class is being optimized.
873
+ */
874
+ static VALUE
875
+ rails_optimized(VALUE self, VALUE clas) {
876
+ ROpt ro = oj_rails_get_opt(&ropts, clas);
877
+
878
+ if (NULL == ro) {
879
+ return Qfalse;
880
+ }
881
+ return (ro->on) ? Qtrue : Qfalse;
882
+ }
883
+
884
+ typedef struct _oo {
885
+ Out out;
886
+ VALUE obj;
887
+ } *OO;
888
+
889
+ static VALUE
890
+ protect_dump(VALUE ov) {
891
+ OO oo = (OO)ov;
892
+
893
+ dump_rails_val(oo->obj, 0, oo->out, true);
894
+
895
+ return Qnil;
896
+ }
897
+
898
+ static VALUE
899
+ encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *argv) {
900
+ char buf[4096];
901
+ struct _out out;
902
+ struct _options copts = *opts;
903
+ volatile VALUE rstr = Qnil;
904
+ struct _oo oo;
905
+ int line = 0;
906
+
907
+ oo.out = &out;
908
+ oo.obj = obj;
909
+ copts.str_rx.head = NULL;
910
+ copts.str_rx.tail = NULL;
911
+ copts.mode = RailsMode;
912
+ if (escape_html) {
913
+ copts.escape_mode = RailsXEsc;
914
+ } else {
915
+ copts.escape_mode = RailsEsc;
916
+ }
917
+ out.buf = buf;
918
+ out.end = buf + sizeof(buf) - 10;
919
+ out.allocated = false;
920
+ out.omit_nil = copts.dump_opts.omit_nil;
921
+ out.caller = 0;
922
+ out.cur = out.buf;
923
+ out.circ_cnt = 0;
924
+ out.opts = &copts;
925
+ out.hash_cnt = 0;
926
+ out.indent = copts.indent;
927
+ out.argc = argc;
928
+ out.argv = argv;
929
+ out.ropts = ropts;
930
+ if (Yes == copts.circular) {
931
+ oj_cache8_new(&out.circ_cache);
932
+ }
933
+ //dump_rails_val(*argv, 0, &out, true);
934
+ rb_protect(protect_dump, (VALUE)&oo, &line);
935
+
936
+ if (0 == line) {
937
+ if (0 < out.indent) {
938
+ switch (*(out.cur - 1)) {
939
+ case ']':
940
+ case '}':
941
+ assure_size(&out, 2);
942
+ *out.cur++ = '\n';
943
+ default:
944
+ break;
945
+ }
946
+ }
947
+ *out.cur = '\0';
948
+
949
+ if (0 == out.buf) {
950
+ rb_raise(rb_eNoMemError, "Not enough memory.");
951
+ }
952
+ rstr = rb_str_new2(out.buf);
953
+ rstr = oj_encode(rstr);
954
+ }
955
+ if (Yes == copts.circular) {
956
+ oj_cache8_delete(out.circ_cache);
957
+ }
958
+ if (out.allocated) {
959
+ xfree(out.buf);
960
+ }
961
+ if (0 != line) {
962
+ rb_jump_tag(line);
963
+ }
964
+ return rstr;
965
+ }
966
+
967
+ /* Document-method: encode
968
+ * call-seq: encode(obj)
969
+ *
970
+ * - *obj* [_Object_] object to encode
971
+ *
972
+ * Returns encoded object as a JSON string.
973
+ */
974
+ static VALUE
975
+ encoder_encode(VALUE self, VALUE obj) {
976
+ Encoder e = (Encoder)DATA_PTR(self);
977
+
978
+ if (Qnil != e->arg) {
979
+ VALUE argv[1] = { e->arg };
980
+
981
+ return encode(obj, &e->ropts, &e->opts, 1, argv);
982
+ }
983
+ return encode(obj, &e->ropts, &e->opts, 0, NULL);
984
+ }
985
+
986
+ /* Document-method: encode
987
+ * call-seq: encode(obj, opts=nil)
988
+ *
989
+ * Encode obj as a JSON String.
990
+ *
991
+ * - *obj* [_Object_|Hash|Array] object to convert to a JSON String
992
+ * - *opts* [_Hash_] options
993
+ *
994
+ * Returns [_String_]
995
+ */
996
+ static VALUE
997
+ rails_encode(int argc, VALUE *argv, VALUE self) {
998
+ if (1 > argc) {
999
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
1000
+ }
1001
+ if (1 == argc) {
1002
+ return encode(*argv, NULL, &oj_default_options, 0, NULL);
1003
+ } else {
1004
+ return encode(*argv, NULL, &oj_default_options, argc - 1, argv + 1);
1005
+ }
1006
+ }
1007
+
1008
+ static VALUE
1009
+ rails_use_standard_json_time_format(VALUE self, VALUE state) {
1010
+ if (Qtrue == state || Qfalse == state) {
1011
+ } else if (Qnil == state) {
1012
+ state = Qfalse;
1013
+ } else {
1014
+ state = Qtrue;
1015
+ }
1016
+ rb_iv_set(self, "@use_standard_json_time_format", state);
1017
+ xml_time = Qtrue == state;
1018
+
1019
+ return state;
1020
+ }
1021
+
1022
+ static VALUE
1023
+ rails_escape_html_entities_in_json(VALUE self, VALUE state) {
1024
+ rb_iv_set(self, "@escape_html_entities_in_json", state);
1025
+ escape_html = Qtrue == state;
1026
+
1027
+ return state;
1028
+ }
1029
+
1030
+ static VALUE
1031
+ rails_time_precision(VALUE self, VALUE prec) {
1032
+ rb_iv_set(self, "@time_precision", prec);
1033
+ oj_default_options.sec_prec = NUM2INT(prec);
1034
+
1035
+ return prec;
1036
+ }
1037
+
1038
+ /* Document-method: set_encoder
1039
+ * call-seq: set_encoder()
1040
+ *
1041
+ * Sets the ActiveSupport.encoder to Oj::Rails::Encoder and wraps some of the
1042
+ * formatting globals used by ActiveSupport to allow the use of those globals
1043
+ * in the Oj::Rails optimizations.
1044
+ */
1045
+ static VALUE
1046
+ rails_set_encoder(VALUE self) {
1047
+ VALUE active;
1048
+ VALUE json;
1049
+ VALUE encoding;
1050
+ VALUE pv;
1051
+ VALUE verbose;
1052
+
1053
+ if (rb_const_defined_at(rb_cObject, rb_intern("ActiveSupport"))) {
1054
+ active = rb_const_get_at(rb_cObject, rb_intern("ActiveSupport"));
1055
+ } else {
1056
+ rb_raise(rb_eStandardError, "ActiveSupport not loaded.");
1057
+ }
1058
+ rb_funcall(active, rb_intern("json_encoder="), 1, encoder_class);
1059
+
1060
+ json = rb_const_get_at(active, rb_intern("JSON"));
1061
+ encoding = rb_const_get_at(json, rb_intern("Encoding"));
1062
+
1063
+ // rb_undef_method doesn't work for modules or maybe sometimes
1064
+ // doesn't. Anyway setting verbose should hide the warning.
1065
+ verbose = rb_gv_get("$VERBOSE");
1066
+ rb_gv_set("$VERBOSE", Qfalse);
1067
+ rb_undef_method(encoding, "use_standard_json_time_format=");
1068
+ rb_define_module_function(encoding, "use_standard_json_time_format=", rails_use_standard_json_time_format, 1);
1069
+
1070
+ rb_undef_method(encoding, "escape_html_entities_in_json=");
1071
+ rb_define_module_function(encoding, "escape_html_entities_in_json=", rails_escape_html_entities_in_json, 1);
1072
+
1073
+ pv = rb_iv_get(encoding, "@time_precision");
1074
+ oj_default_options.sec_prec = NUM2INT(pv);
1075
+ rb_undef_method(encoding, "time_precision=");
1076
+ rb_define_module_function(encoding, "time_precision=", rails_time_precision, 1);
1077
+ rb_gv_set("$VERBOSE", verbose);
1078
+
1079
+ return Qnil;
1080
+ }
1081
+
1082
+ /* Document-method: set_decoder
1083
+ * call-seq: set_decoder()
1084
+ *
1085
+ * Sets the JSON.parse function to be the Oj::parse function which is json gem
1086
+ * compatible.
1087
+ */
1088
+ static VALUE
1089
+ rails_set_decoder(VALUE self) {
1090
+ VALUE json;
1091
+ VALUE json_error;
1092
+ VALUE verbose;
1093
+
1094
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
1095
+ json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
1096
+ } else {
1097
+ json = rb_define_module("JSON");
1098
+ }
1099
+ if (rb_const_defined_at(json, rb_intern("JSONError"))) {
1100
+ json_error = rb_const_get(json, rb_intern("JSONError"));
1101
+ } else {
1102
+ json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
1103
+ }
1104
+ if (rb_const_defined_at(json, rb_intern("ParserError"))) {
1105
+ oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
1106
+ } else {
1107
+ oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
1108
+ }
1109
+ // rb_undef_method doesn't work for modules or maybe sometimes
1110
+ // doesn't. Anyway setting verbose should hide the warning.
1111
+ verbose = rb_gv_get("$VERBOSE");
1112
+ rb_gv_set("$VERBOSE", Qfalse);
1113
+ rb_undef_method(json, "parse");
1114
+ rb_define_module_function(json, "parse", oj_mimic_parse, -1);
1115
+ rb_gv_set("$VERBOSE", verbose);
1116
+
1117
+ return Qnil;
1118
+ }
1119
+
1120
+ /* Document-module: Oj.optimize_rails()
1121
+ *
1122
+ * Sets the Oj as the Rails encoder and decoder. Oj::Rails.optimize is also
1123
+ * called.
1124
+ */
1125
+ VALUE
1126
+ oj_optimize_rails(VALUE self) {
1127
+ rails_set_encoder(self);
1128
+ rails_set_decoder(self);
1129
+ rails_optimize(0, NULL, self);
1130
+ rails_mimic_json(self);
1131
+
1132
+ return Qnil;
1133
+ }
1134
+
1135
+ /* Document-module: Oj::Rails
1136
+ *
1137
+ * Module that provides rails and active support compatibility.
1138
+ */
1139
+ /* Document-class: Oj::Rails::Encoder
1140
+ *
1141
+ * The Oj ActiveSupport compliant encoder.
1142
+ */
1143
+ void
1144
+ oj_mimic_rails_init() {
1145
+ VALUE rails = rb_define_module_under(Oj, "Rails");
1146
+
1147
+ rb_define_module_function(rails, "encode", rails_encode, -1);
1148
+
1149
+ encoder_class = rb_define_class_under(rails, "Encoder", rb_cObject);
1150
+ rb_define_module_function(encoder_class, "new", encoder_new, -1);
1151
+ rb_define_module_function(rails, "optimize", rails_optimize, -1);
1152
+ rb_define_module_function(rails, "deoptimize", rails_deoptimize, -1);
1153
+ rb_define_module_function(rails, "optimized?", rails_optimized, 1);
1154
+ rb_define_module_function(rails, "mimic_JSON", rails_mimic_json, 0);
1155
+
1156
+ rb_define_module_function(rails, "set_encoder", rails_set_encoder, 0);
1157
+ rb_define_module_function(rails, "set_decoder", rails_set_decoder, 0);
1158
+
1159
+ rb_define_method(encoder_class, "encode", encoder_encode, 1);
1160
+ rb_define_method(encoder_class, "optimize", encoder_optimize, -1);
1161
+ rb_define_method(encoder_class, "deoptimize", encoder_deoptimize, -1);
1162
+ rb_define_method(encoder_class, "optimized?", encoder_optimized, 1);
1163
+ }
1164
+
1165
+ static void
1166
+ dump_to_hash(VALUE obj, int depth, Out out) {
1167
+ dump_rails_val(rb_funcall(obj, oj_to_hash_id, 0), depth, out, true);
1168
+ }
1169
+
1170
+ static void
1171
+ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1172
+ char buf[64];
1173
+ char *b;
1174
+ double d = rb_num2dbl(obj);
1175
+ int cnt = 0;
1176
+
1177
+ if (0.0 == d) {
1178
+ b = buf;
1179
+ *b++ = '0';
1180
+ *b++ = '.';
1181
+ *b++ = '0';
1182
+ *b++ = '\0';
1183
+ cnt = 3;
1184
+ } else {
1185
+ if (isnan(d) || OJ_INFINITY == d || -OJ_INFINITY == d) {
1186
+ strcpy(buf, "null");
1187
+ cnt = 4;
1188
+ } else if (d == (double)(long long int)d) {
1189
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1190
+ } else if (oj_rails_float_opt) {
1191
+ cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, "%0.16g");
1192
+ } else {
1193
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1194
+
1195
+ strcpy(buf, rb_string_value_ptr((VALUE*)&rstr));
1196
+ cnt = (int)RSTRING_LEN(rstr);
1197
+ }
1198
+ }
1199
+ assure_size(out, cnt);
1200
+ for (b = buf; '\0' != *b; b++) {
1201
+ *out->cur++ = *b;
1202
+ }
1203
+ *out->cur = '\0';
1204
+ }
1205
+
1206
+ static void
1207
+ dump_array(VALUE a, int depth, Out out, bool as_ok) {
1208
+ size_t size;
1209
+ int i, cnt;
1210
+ int d2 = depth + 1;
1211
+
1212
+ if (Yes == out->opts->circular) {
1213
+ if (0 > oj_check_circular(a, out)) {
1214
+ oj_dump_nil(Qnil, 0, out, false);
1215
+ return;
1216
+ }
1217
+ }
1218
+ //if (!oj_rails_array_opt && as_ok && 0 < out->argc && rb_respond_to(a, oj_as_json_id)) {
1219
+ if (as_ok && 0 < out->argc && rb_respond_to(a, oj_as_json_id)) {
1220
+ dump_as_json(a, depth, out, false);
1221
+ return;
1222
+ }
1223
+ cnt = (int)RARRAY_LEN(a);
1224
+ *out->cur++ = '[';
1225
+ size = 2;
1226
+ assure_size(out, size);
1227
+ if (0 == cnt) {
1228
+ *out->cur++ = ']';
1229
+ } else {
1230
+ if (out->opts->dump_opts.use) {
1231
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
1232
+ } else {
1233
+ size = d2 * out->indent + 2;
1234
+ }
1235
+ cnt--;
1236
+ for (i = 0; i <= cnt; i++) {
1237
+ assure_size(out, size);
1238
+ if (out->opts->dump_opts.use) {
1239
+ if (0 < out->opts->dump_opts.array_size) {
1240
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
1241
+ out->cur += out->opts->dump_opts.array_size;
1242
+ }
1243
+ if (0 < out->opts->dump_opts.indent_size) {
1244
+ int i;
1245
+ for (i = d2; 0 < i; i--) {
1246
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1247
+ out->cur += out->opts->dump_opts.indent_size;
1248
+ }
1249
+ }
1250
+ } else {
1251
+ fill_indent(out, d2);
1252
+ }
1253
+ dump_rails_val(rb_ary_entry(a, i), d2, out, true);
1254
+ if (i < cnt) {
1255
+ *out->cur++ = ',';
1256
+ }
1257
+ }
1258
+ size = depth * out->indent + 1;
1259
+ assure_size(out, size);
1260
+ if (out->opts->dump_opts.use) {
1261
+ if (0 < out->opts->dump_opts.array_size) {
1262
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
1263
+ out->cur += out->opts->dump_opts.array_size;
1264
+ }
1265
+ if (0 < out->opts->dump_opts.indent_size) {
1266
+ int i;
1267
+
1268
+ for (i = depth; 0 < i; i--) {
1269
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1270
+ out->cur += out->opts->dump_opts.indent_size;
1271
+ }
1272
+ }
1273
+ } else {
1274
+ fill_indent(out, depth);
1275
+ }
1276
+ *out->cur++ = ']';
1277
+ }
1278
+ *out->cur = '\0';
1279
+ }
1280
+
1281
+ static int
1282
+ hash_cb(VALUE key, VALUE value, Out out) {
1283
+ int depth = out->depth;
1284
+ long size;
1285
+ int rtype = rb_type(key);
1286
+
1287
+ if (out->omit_nil && Qnil == value) {
1288
+ return ST_CONTINUE;
1289
+ }
1290
+ if (rtype != T_STRING && rtype != T_SYMBOL) {
1291
+ key = rb_funcall(key, oj_to_s_id, 0);
1292
+ rtype = rb_type(key);
1293
+ }
1294
+ if (!out->opts->dump_opts.use) {
1295
+ size = depth * out->indent + 1;
1296
+ assure_size(out, size);
1297
+ fill_indent(out, depth);
1298
+ if (rtype == T_STRING) {
1299
+ oj_dump_str(key, 0, out, false);
1300
+ } else {
1301
+ oj_dump_sym(key, 0, out, false);
1302
+ }
1303
+ *out->cur++ = ':';
1304
+ } else {
1305
+ size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
1306
+ assure_size(out, size);
1307
+ if (0 < out->opts->dump_opts.hash_size) {
1308
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
1309
+ out->cur += out->opts->dump_opts.hash_size;
1310
+ }
1311
+ if (0 < out->opts->dump_opts.indent_size) {
1312
+ int i;
1313
+ for (i = depth; 0 < i; i--) {
1314
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1315
+ out->cur += out->opts->dump_opts.indent_size;
1316
+ }
1317
+ }
1318
+ if (rtype == T_STRING) {
1319
+ oj_dump_str(key, 0, out, false);
1320
+ } else {
1321
+ oj_dump_sym(key, 0, out, false);
1322
+ }
1323
+ size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
1324
+ assure_size(out, size);
1325
+ if (0 < out->opts->dump_opts.before_size) {
1326
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
1327
+ out->cur += out->opts->dump_opts.before_size;
1328
+ }
1329
+ *out->cur++ = ':';
1330
+ if (0 < out->opts->dump_opts.after_size) {
1331
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
1332
+ out->cur += out->opts->dump_opts.after_size;
1333
+ }
1334
+ }
1335
+ dump_rails_val(value, depth, out, true);
1336
+ out->depth = depth;
1337
+ *out->cur++ = ',';
1338
+
1339
+ return ST_CONTINUE;
1340
+ }
1341
+
1342
+ static void
1343
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
1344
+ int cnt;
1345
+ size_t size;
1346
+
1347
+ if (Yes == out->opts->circular) {
1348
+ if (0 > oj_check_circular(obj, out)) {
1349
+ oj_dump_nil(Qnil, 0, out, false);
1350
+ return;
1351
+ }
1352
+ }
1353
+ if ((!oj_rails_hash_opt || 0 < out->argc) && as_ok && rb_respond_to(obj, oj_as_json_id)) {
1354
+ dump_as_json(obj, depth, out, false);
1355
+ return;
1356
+ }
1357
+ cnt = (int)RHASH_SIZE(obj);
1358
+ size = depth * out->indent + 2;
1359
+ assure_size(out, 2);
1360
+ *out->cur++ = '{';
1361
+ if (0 == cnt) {
1362
+ *out->cur++ = '}';
1363
+ } else {
1364
+ out->depth = depth + 1;
1365
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
1366
+ if (',' == *(out->cur - 1)) {
1367
+ out->cur--; // backup to overwrite last comma
1368
+ }
1369
+ if (!out->opts->dump_opts.use) {
1370
+ assure_size(out, size);
1371
+ fill_indent(out, depth);
1372
+ } else {
1373
+ size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
1374
+ assure_size(out, size);
1375
+ if (0 < out->opts->dump_opts.hash_size) {
1376
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
1377
+ out->cur += out->opts->dump_opts.hash_size;
1378
+ }
1379
+ if (0 < out->opts->dump_opts.indent_size) {
1380
+ int i;
1381
+
1382
+ for (i = depth; 0 < i; i--) {
1383
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
1384
+ out->cur += out->opts->dump_opts.indent_size;
1385
+ }
1386
+ }
1387
+ }
1388
+ *out->cur++ = '}';
1389
+ }
1390
+ *out->cur = '\0';
1391
+ }
1392
+
1393
+ static void
1394
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
1395
+ if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
1396
+ out->argc = 0;
1397
+ return;
1398
+ }
1399
+ if (as_ok) {
1400
+ ROpt ro;
1401
+
1402
+ if (NULL != (ro = oj_rails_get_opt(out->ropts, rb_obj_class(obj))) && ro->on) {
1403
+ ro->dump(obj, depth, out, as_ok);
1404
+ } else if (rb_respond_to(obj, oj_as_json_id)) {
1405
+ dump_as_json(obj, depth, out, true);
1406
+ } else if (rb_respond_to(obj, oj_to_hash_id)) {
1407
+ dump_to_hash(obj, depth, out);
1408
+ } else {
1409
+ oj_dump_obj_to_s(obj, out);
1410
+ }
1411
+ } else if (rb_respond_to(obj, oj_to_hash_id)) {
1412
+ // Always attempt to_hash.
1413
+ dump_to_hash(obj, depth, out);
1414
+ } else {
1415
+ oj_dump_obj_to_s(obj, out);
1416
+ }
1417
+ }
1418
+
1419
+ static DumpFunc rails_funcs[] = {
1420
+ NULL, // RUBY_T_NONE = 0x00,
1421
+ dump_obj, // RUBY_T_OBJECT = 0x01,
1422
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
1423
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
1424
+ dump_float, // RUBY_T_FLOAT = 0x04,
1425
+ oj_dump_str, // RUBY_T_STRING = 0x05,
1426
+ dump_regexp, // RUBY_T_REGEXP = 0x06,
1427
+ //dump_as_string, // RUBY_T_REGEXP = 0x06,
1428
+ dump_array, // RUBY_T_ARRAY = 0x07,
1429
+ dump_hash, // RUBY_T_HASH = 0x08,
1430
+ dump_obj, // RUBY_T_STRUCT = 0x09,
1431
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
1432
+ NULL, // RUBY_T_FILE = 0x0b,
1433
+ dump_obj, // RUBY_T_DATA = 0x0c,
1434
+ NULL, // RUBY_T_MATCH = 0x0d,
1435
+ // Rails raises a stack error on Complex and Rational. It also corrupts
1436
+ // something which causes a segfault on the next call. Oj will not mimic
1437
+ // that behavior.
1438
+ dump_as_string, // RUBY_T_COMPLEX = 0x0e,
1439
+ dump_as_string, // RUBY_T_RATIONAL = 0x0f,
1440
+ NULL, // 0x10
1441
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
1442
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
1443
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
1444
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
1445
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
1446
+ };
1447
+
1448
+ static void
1449
+ dump_rails_val(VALUE obj, int depth, Out out, bool as_ok) {
1450
+ int type = rb_type(obj);
1451
+
1452
+ if (Yes == out->opts->trace) {
1453
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
1454
+ }
1455
+ if (MAX_DEPTH < depth) {
1456
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
1457
+ }
1458
+ if (0 < type && type <= RUBY_T_FIXNUM) {
1459
+ DumpFunc f = rails_funcs[type];
1460
+
1461
+ if (NULL != f) {
1462
+ f(obj, depth, out, as_ok);
1463
+ if (Yes == out->opts->trace) {
1464
+ oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
1465
+ }
1466
+ return;
1467
+ }
1468
+ }
1469
+ oj_dump_nil(Qnil, depth, out, false);
1470
+ if (Yes == out->opts->trace) {
1471
+ oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
1472
+ }
1473
+ }
1474
+
1475
+ void
1476
+ oj_dump_rails_val(VALUE obj, int depth, Out out) {
1477
+ out->opts->str_rx.head = NULL;
1478
+ out->opts->str_rx.tail = NULL;
1479
+ if (escape_html) {
1480
+ out->opts->escape_mode = RailsXEsc;
1481
+ } else {
1482
+ out->opts->escape_mode = RailsEsc;
1483
+ }
1484
+ dump_rails_val(obj, depth, out, true);
1485
+ }