oj 3.10.7

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