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