oj 3.7.12
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +96 -0
- data/ext/oj/buf.h +103 -0
- data/ext/oj/cache8.c +107 -0
- data/ext/oj/cache8.h +48 -0
- data/ext/oj/circarray.c +68 -0
- data/ext/oj/circarray.h +23 -0
- data/ext/oj/code.c +235 -0
- data/ext/oj/code.h +42 -0
- data/ext/oj/compat.c +299 -0
- data/ext/oj/custom.c +1188 -0
- data/ext/oj/dump.c +1232 -0
- data/ext/oj/dump.h +94 -0
- data/ext/oj/dump_compat.c +973 -0
- data/ext/oj/dump_leaf.c +252 -0
- data/ext/oj/dump_object.c +837 -0
- data/ext/oj/dump_strict.c +433 -0
- data/ext/oj/encode.h +45 -0
- data/ext/oj/err.c +57 -0
- data/ext/oj/err.h +70 -0
- data/ext/oj/extconf.rb +47 -0
- data/ext/oj/fast.c +1771 -0
- data/ext/oj/hash.c +163 -0
- data/ext/oj/hash.h +46 -0
- data/ext/oj/hash_test.c +512 -0
- data/ext/oj/mimic_json.c +873 -0
- data/ext/oj/object.c +771 -0
- data/ext/oj/odd.c +231 -0
- data/ext/oj/odd.h +44 -0
- data/ext/oj/oj.c +1694 -0
- data/ext/oj/oj.h +381 -0
- data/ext/oj/parse.c +1085 -0
- data/ext/oj/parse.h +111 -0
- data/ext/oj/rails.c +1485 -0
- data/ext/oj/rails.h +21 -0
- data/ext/oj/reader.c +231 -0
- data/ext/oj/reader.h +151 -0
- data/ext/oj/resolve.c +102 -0
- data/ext/oj/resolve.h +14 -0
- data/ext/oj/rxclass.c +147 -0
- data/ext/oj/rxclass.h +27 -0
- data/ext/oj/saj.c +714 -0
- data/ext/oj/scp.c +224 -0
- data/ext/oj/sparse.c +910 -0
- data/ext/oj/stream_writer.c +363 -0
- data/ext/oj/strict.c +212 -0
- data/ext/oj/string_writer.c +512 -0
- data/ext/oj/trace.c +79 -0
- data/ext/oj/trace.h +28 -0
- data/ext/oj/util.c +136 -0
- data/ext/oj/util.h +19 -0
- data/ext/oj/val_stack.c +118 -0
- data/ext/oj/val_stack.h +185 -0
- data/ext/oj/wab.c +631 -0
- data/lib/oj.rb +21 -0
- data/lib/oj/active_support_helper.rb +41 -0
- data/lib/oj/bag.rb +88 -0
- data/lib/oj/easy_hash.rb +52 -0
- data/lib/oj/error.rb +22 -0
- data/lib/oj/json.rb +176 -0
- data/lib/oj/mimic.rb +267 -0
- data/lib/oj/saj.rb +66 -0
- data/lib/oj/schandler.rb +142 -0
- data/lib/oj/state.rb +131 -0
- data/lib/oj/version.rb +5 -0
- 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/JsonGem.md +79 -0
- data/pages/Modes.md +154 -0
- data/pages/Options.md +266 -0
- data/pages/Rails.md +116 -0
- data/pages/Security.md +20 -0
- data/pages/WAB.md +13 -0
- data/test/_test_active.rb +76 -0
- data/test/_test_active_mimic.rb +96 -0
- data/test/_test_mimic_rails.rb +126 -0
- data/test/activerecord/result_test.rb +27 -0
- data/test/activesupport4/decoding_test.rb +108 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/decoding_test.rb +125 -0
- data/test/activesupport5/encoding_test.rb +485 -0
- data/test/activesupport5/encoding_test_cases.rb +90 -0
- data/test/activesupport5/test_helper.rb +50 -0
- data/test/activesupport5/time_zone_test_helpers.rb +24 -0
- data/test/big.rb +15 -0
- data/test/files.rb +29 -0
- data/test/foo.rb +33 -0
- data/test/helper.rb +26 -0
- data/test/isolated/shared.rb +308 -0
- data/test/isolated/test_mimic_after.rb +13 -0
- data/test/isolated/test_mimic_alone.rb +12 -0
- data/test/isolated/test_mimic_as_json.rb +45 -0
- data/test/isolated/test_mimic_before.rb +13 -0
- data/test/isolated/test_mimic_define.rb +28 -0
- data/test/isolated/test_mimic_rails_after.rb +22 -0
- data/test/isolated/test_mimic_rails_before.rb +21 -0
- data/test/isolated/test_mimic_redefine.rb +15 -0
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +148 -0
- data/test/json_gem/json_encoding_test.rb +107 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +383 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +18 -0
- data/test/mem.rb +35 -0
- data/test/perf.rb +107 -0
- data/test/perf_compat.rb +130 -0
- data/test/perf_fast.rb +164 -0
- data/test/perf_file.rb +64 -0
- data/test/perf_object.rb +138 -0
- data/test/perf_saj.rb +109 -0
- data/test/perf_scp.rb +151 -0
- data/test/perf_simple.rb +287 -0
- data/test/perf_strict.rb +145 -0
- data/test/perf_wab.rb +131 -0
- data/test/sample.rb +54 -0
- data/test/sample/change.rb +14 -0
- data/test/sample/dir.rb +19 -0
- data/test/sample/doc.rb +36 -0
- data/test/sample/file.rb +48 -0
- data/test/sample/group.rb +16 -0
- data/test/sample/hasprops.rb +16 -0
- data/test/sample/layer.rb +12 -0
- data/test/sample/line.rb +20 -0
- data/test/sample/oval.rb +10 -0
- data/test/sample/rect.rb +10 -0
- data/test/sample/shape.rb +35 -0
- data/test/sample/text.rb +20 -0
- data/test/sample_json.rb +37 -0
- data/test/test_compat.rb +509 -0
- data/test/test_custom.rb +406 -0
- data/test/test_debian.rb +53 -0
- data/test/test_fast.rb +470 -0
- data/test/test_file.rb +239 -0
- data/test/test_gc.rb +49 -0
- data/test/test_hash.rb +29 -0
- data/test/test_integer_range.rb +73 -0
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +1018 -0
- data/test/test_saj.rb +186 -0
- data/test/test_scp.rb +433 -0
- data/test/test_strict.rb +410 -0
- data/test/test_various.rb +739 -0
- data/test/test_wab.rb +307 -0
- data/test/test_writer.rb +380 -0
- data/test/tests.rb +24 -0
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- metadata +359 -0
data/ext/oj/dump.c
ADDED
|
@@ -0,0 +1,1232 @@
|
|
|
1
|
+
/* dump.c
|
|
2
|
+
* Copyright (c) 2012, 2017, Peter Ohler
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <math.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include <stdio.h>
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
#include <unistd.h>
|
|
13
|
+
|
|
14
|
+
#include "oj.h"
|
|
15
|
+
#include "cache8.h"
|
|
16
|
+
#include "dump.h"
|
|
17
|
+
#include "odd.h"
|
|
18
|
+
#include "util.h"
|
|
19
|
+
|
|
20
|
+
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
|
21
|
+
#define OJ_INFINITY (1.0/0.0)
|
|
22
|
+
|
|
23
|
+
#define MAX_DEPTH 1000
|
|
24
|
+
|
|
25
|
+
static const char inf_val[] = INF_VAL;
|
|
26
|
+
static const char ninf_val[] = NINF_VAL;
|
|
27
|
+
static const char nan_val[] = NAN_VAL;
|
|
28
|
+
|
|
29
|
+
typedef unsigned long ulong;
|
|
30
|
+
|
|
31
|
+
static size_t hibit_friendly_size(const uint8_t *str, size_t len);
|
|
32
|
+
static size_t xss_friendly_size(const uint8_t *str, size_t len);
|
|
33
|
+
static size_t ascii_friendly_size(const uint8_t *str, size_t len);
|
|
34
|
+
|
|
35
|
+
static const char hex_chars[17] = "0123456789abcdef";
|
|
36
|
+
|
|
37
|
+
// JSON standard except newlines are no escaped
|
|
38
|
+
static char newline_friendly_chars[256] = "\
|
|
39
|
+
66666666221622666666666666666666\
|
|
40
|
+
11211111111111111111111111111111\
|
|
41
|
+
11111111111111111111111111112111\
|
|
42
|
+
11111111111111111111111111111111\
|
|
43
|
+
11111111111111111111111111111111\
|
|
44
|
+
11111111111111111111111111111111\
|
|
45
|
+
11111111111111111111111111111111\
|
|
46
|
+
11111111111111111111111111111111";
|
|
47
|
+
|
|
48
|
+
// JSON standard
|
|
49
|
+
static char hibit_friendly_chars[256] = "\
|
|
50
|
+
66666666222622666666666666666666\
|
|
51
|
+
11211111111111111111111111111111\
|
|
52
|
+
11111111111111111111111111112111\
|
|
53
|
+
11111111111111111111111111111111\
|
|
54
|
+
11111111111111111111111111111111\
|
|
55
|
+
11111111111111111111111111111111\
|
|
56
|
+
11111111111111111111111111111111\
|
|
57
|
+
11111111111111111111111111111111";
|
|
58
|
+
|
|
59
|
+
// High bit set characters are always encoded as unicode. Worse case is 3
|
|
60
|
+
// bytes per character in the output. That makes this conservative.
|
|
61
|
+
static char ascii_friendly_chars[256] = "\
|
|
62
|
+
66666666222622666666666666666666\
|
|
63
|
+
11211111111111111111111111111111\
|
|
64
|
+
11111111111111111111111111112111\
|
|
65
|
+
11111111111111111111111111111116\
|
|
66
|
+
33333333333333333333333333333333\
|
|
67
|
+
33333333333333333333333333333333\
|
|
68
|
+
33333333333333333333333333333333\
|
|
69
|
+
33333333333333333333333333333333";
|
|
70
|
+
|
|
71
|
+
// XSS safe mode
|
|
72
|
+
static char xss_friendly_chars[256] = "\
|
|
73
|
+
66666666222622666666666666666666\
|
|
74
|
+
11211161111111121111111111116161\
|
|
75
|
+
11111111111111111111111111112111\
|
|
76
|
+
11111111111111111111111111111116\
|
|
77
|
+
33333333333333333333333333333333\
|
|
78
|
+
33333333333333333333333333333333\
|
|
79
|
+
33333333333333333333333333333333\
|
|
80
|
+
33333333333333333333333333333333";
|
|
81
|
+
|
|
82
|
+
// JSON XSS combo
|
|
83
|
+
static char hixss_friendly_chars[256] = "\
|
|
84
|
+
66666666222622666666666666666666\
|
|
85
|
+
11211111111111111111111111111111\
|
|
86
|
+
11111111111111111111111111112111\
|
|
87
|
+
11111111111111111111111111111111\
|
|
88
|
+
11111111111111111111111111111111\
|
|
89
|
+
11111111111111111111111111111111\
|
|
90
|
+
11111111111111111111111111111111\
|
|
91
|
+
11611111111111111111111111111111";
|
|
92
|
+
|
|
93
|
+
// Rails XSS combo
|
|
94
|
+
static char rails_xss_friendly_chars[256] = "\
|
|
95
|
+
66666666222622666666666666666666\
|
|
96
|
+
11211161111111111111111111116161\
|
|
97
|
+
11111111111111111111111111112111\
|
|
98
|
+
11111111111111111111111111111111\
|
|
99
|
+
11111111111111111111111111111111\
|
|
100
|
+
11111111111111111111111111111111\
|
|
101
|
+
11111111111111111111111111111111\
|
|
102
|
+
11611111111111111111111111111111";
|
|
103
|
+
|
|
104
|
+
// Rails HTML non-escape
|
|
105
|
+
static char rails_friendly_chars[256] = "\
|
|
106
|
+
66666666222622666666666666666666\
|
|
107
|
+
11211111111111111111111111111111\
|
|
108
|
+
11111111111111111111111111112111\
|
|
109
|
+
11111111111111111111111111111111\
|
|
110
|
+
11111111111111111111111111111111\
|
|
111
|
+
11111111111111111111111111111111\
|
|
112
|
+
11111111111111111111111111111111\
|
|
113
|
+
11611111111111111111111111111111";
|
|
114
|
+
|
|
115
|
+
static void
|
|
116
|
+
raise_strict(VALUE obj) {
|
|
117
|
+
rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.", rb_class2name(rb_obj_class(obj)));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
inline static size_t
|
|
121
|
+
newline_friendly_size(const uint8_t *str, size_t len) {
|
|
122
|
+
size_t size = 0;
|
|
123
|
+
size_t i = len;
|
|
124
|
+
|
|
125
|
+
for (; 0 < i; str++, i--) {
|
|
126
|
+
size += newline_friendly_chars[*str];
|
|
127
|
+
}
|
|
128
|
+
return size - len * (size_t)'0';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
inline static size_t
|
|
132
|
+
hibit_friendly_size(const uint8_t *str, size_t len) {
|
|
133
|
+
size_t size = 0;
|
|
134
|
+
size_t i = len;
|
|
135
|
+
|
|
136
|
+
for (; 0 < i; str++, i--) {
|
|
137
|
+
size += hibit_friendly_chars[*str];
|
|
138
|
+
}
|
|
139
|
+
return size - len * (size_t)'0';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
inline static size_t
|
|
143
|
+
ascii_friendly_size(const uint8_t *str, size_t len) {
|
|
144
|
+
size_t size = 0;
|
|
145
|
+
size_t i = len;
|
|
146
|
+
|
|
147
|
+
for (; 0 < i; str++, i--) {
|
|
148
|
+
size += ascii_friendly_chars[*str];
|
|
149
|
+
}
|
|
150
|
+
return size - len * (size_t)'0';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
inline static size_t
|
|
154
|
+
xss_friendly_size(const uint8_t *str, size_t len) {
|
|
155
|
+
size_t size = 0;
|
|
156
|
+
size_t i = len;
|
|
157
|
+
|
|
158
|
+
for (; 0 < i; str++, i--) {
|
|
159
|
+
size += xss_friendly_chars[*str];
|
|
160
|
+
}
|
|
161
|
+
return size - len * (size_t)'0';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
inline static size_t
|
|
165
|
+
hixss_friendly_size(const uint8_t *str, size_t len) {
|
|
166
|
+
size_t size = 0;
|
|
167
|
+
size_t i = len;
|
|
168
|
+
bool check = false;
|
|
169
|
+
|
|
170
|
+
for (; 0 < i; str++, i--) {
|
|
171
|
+
size += hixss_friendly_chars[*str];
|
|
172
|
+
if (0 != (0x80 & *str)) {
|
|
173
|
+
check = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return size - len * (size_t)'0' + check;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
inline static size_t
|
|
180
|
+
rails_xss_friendly_size(const uint8_t *str, size_t len) {
|
|
181
|
+
size_t size = 0;
|
|
182
|
+
size_t i = len;
|
|
183
|
+
|
|
184
|
+
for (; 0 < i; str++, i--) {
|
|
185
|
+
size += rails_xss_friendly_chars[*str];
|
|
186
|
+
}
|
|
187
|
+
return size - len * (size_t)'0';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
inline static size_t
|
|
191
|
+
rails_friendly_size(const uint8_t *str, size_t len) {
|
|
192
|
+
size_t size = 0;
|
|
193
|
+
size_t i = len;
|
|
194
|
+
|
|
195
|
+
for (; 0 < i; str++, i--) {
|
|
196
|
+
size += rails_friendly_chars[*str];
|
|
197
|
+
}
|
|
198
|
+
return size - len * (size_t)'0';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const char*
|
|
202
|
+
oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
|
|
203
|
+
const char *str = NULL;
|
|
204
|
+
|
|
205
|
+
if (AutoNan == opt) {
|
|
206
|
+
switch (mode) {
|
|
207
|
+
case CompatMode: opt = WordNan; break;
|
|
208
|
+
case StrictMode: opt = RaiseNan; break;
|
|
209
|
+
default: break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
switch (opt) {
|
|
213
|
+
case RaiseNan:
|
|
214
|
+
raise_strict(obj);
|
|
215
|
+
break;
|
|
216
|
+
case WordNan:
|
|
217
|
+
if (plus) {
|
|
218
|
+
str = "Infinity";
|
|
219
|
+
*lenp = 8;
|
|
220
|
+
} else {
|
|
221
|
+
str = "-Infinity";
|
|
222
|
+
*lenp = 9;
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
case NullNan:
|
|
226
|
+
str = "null";
|
|
227
|
+
*lenp = 4;
|
|
228
|
+
break;
|
|
229
|
+
case HugeNan:
|
|
230
|
+
default:
|
|
231
|
+
if (plus) {
|
|
232
|
+
str = inf_val;
|
|
233
|
+
*lenp = sizeof(inf_val) - 1;
|
|
234
|
+
} else {
|
|
235
|
+
str = ninf_val;
|
|
236
|
+
*lenp = sizeof(ninf_val) - 1;
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
return str;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
inline static void
|
|
244
|
+
dump_hex(uint8_t c, Out out) {
|
|
245
|
+
uint8_t d = (c >> 4) & 0x0F;
|
|
246
|
+
|
|
247
|
+
*out->cur++ = hex_chars[d];
|
|
248
|
+
d = c & 0x0F;
|
|
249
|
+
*out->cur++ = hex_chars[d];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
static void
|
|
253
|
+
raise_invalid_unicode(const char *str, int len, int pos) {
|
|
254
|
+
char buf[len + 1];
|
|
255
|
+
char c;
|
|
256
|
+
char code[32];
|
|
257
|
+
char *cp = code;
|
|
258
|
+
int i;
|
|
259
|
+
uint8_t d;
|
|
260
|
+
|
|
261
|
+
*cp++ = '[';
|
|
262
|
+
for (i = pos; i < len && i - pos < 5; i++) {
|
|
263
|
+
c = str[i];
|
|
264
|
+
d = (c >> 4) & 0x0F;
|
|
265
|
+
*cp++ = hex_chars[d];
|
|
266
|
+
d = c & 0x0F;
|
|
267
|
+
*cp++ = hex_chars[d];
|
|
268
|
+
*cp++ = ' ';
|
|
269
|
+
}
|
|
270
|
+
cp--;
|
|
271
|
+
*cp++ = ']';
|
|
272
|
+
*cp = '\0';
|
|
273
|
+
strncpy(buf, str, len);
|
|
274
|
+
rb_raise(oj_json_generator_error_class, "Invalid Unicode %s at %d in '%s'", code, pos, buf);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
static const char*
|
|
278
|
+
dump_unicode(const char *str, const char *end, Out out, const char *orig) {
|
|
279
|
+
uint32_t code = 0;
|
|
280
|
+
uint8_t b = *(uint8_t*)str;
|
|
281
|
+
int i, cnt;
|
|
282
|
+
|
|
283
|
+
if (0xC0 == (0xE0 & b)) {
|
|
284
|
+
cnt = 1;
|
|
285
|
+
code = b & 0x0000001F;
|
|
286
|
+
} else if (0xE0 == (0xF0 & b)) {
|
|
287
|
+
cnt = 2;
|
|
288
|
+
code = b & 0x0000000F;
|
|
289
|
+
} else if (0xF0 == (0xF8 & b)) {
|
|
290
|
+
cnt = 3;
|
|
291
|
+
code = b & 0x00000007;
|
|
292
|
+
} else if (0xF8 == (0xFC & b)) {
|
|
293
|
+
cnt = 4;
|
|
294
|
+
code = b & 0x00000003;
|
|
295
|
+
} else if (0xFC == (0xFE & b)) {
|
|
296
|
+
cnt = 5;
|
|
297
|
+
code = b & 0x00000001;
|
|
298
|
+
} else {
|
|
299
|
+
cnt = 0;
|
|
300
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
301
|
+
}
|
|
302
|
+
str++;
|
|
303
|
+
for (; 0 < cnt; cnt--, str++) {
|
|
304
|
+
b = *(uint8_t*)str;
|
|
305
|
+
if (end <= str || 0x80 != (0xC0 & b)) {
|
|
306
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
307
|
+
}
|
|
308
|
+
code = (code << 6) | (b & 0x0000003F);
|
|
309
|
+
}
|
|
310
|
+
if (0x0000FFFF < code) {
|
|
311
|
+
uint32_t c1;
|
|
312
|
+
|
|
313
|
+
code -= 0x00010000;
|
|
314
|
+
c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
|
|
315
|
+
code = (code & 0x000003FF) + 0x0000DC00;
|
|
316
|
+
*out->cur++ = '\\';
|
|
317
|
+
*out->cur++ = 'u';
|
|
318
|
+
for (i = 3; 0 <= i; i--) {
|
|
319
|
+
*out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
*out->cur++ = '\\';
|
|
323
|
+
*out->cur++ = 'u';
|
|
324
|
+
for (i = 3; 0 <= i; i--) {
|
|
325
|
+
*out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
|
|
326
|
+
}
|
|
327
|
+
return str - 1;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
static const char*
|
|
331
|
+
check_unicode(const char *str, const char *end, const char *orig) {
|
|
332
|
+
uint8_t b = *(uint8_t*)str;
|
|
333
|
+
int cnt = 0;
|
|
334
|
+
|
|
335
|
+
if (0xC0 == (0xE0 & b)) {
|
|
336
|
+
cnt = 1;
|
|
337
|
+
} else if (0xE0 == (0xF0 & b)) {
|
|
338
|
+
cnt = 2;
|
|
339
|
+
} else if (0xF0 == (0xF8 & b)) {
|
|
340
|
+
cnt = 3;
|
|
341
|
+
} else if (0xF8 == (0xFC & b)) {
|
|
342
|
+
cnt = 4;
|
|
343
|
+
} else if (0xFC == (0xFE & b)) {
|
|
344
|
+
cnt = 5;
|
|
345
|
+
} else {
|
|
346
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
347
|
+
}
|
|
348
|
+
str++;
|
|
349
|
+
for (; 0 < cnt; cnt--, str++) {
|
|
350
|
+
b = *(uint8_t*)str;
|
|
351
|
+
if (end <= str || 0x80 != (0xC0 & b)) {
|
|
352
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return str;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Returns 0 if not using circular references, -1 if no further writing is
|
|
359
|
+
// needed (duplicate), and a positive value if the object was added to the
|
|
360
|
+
// cache.
|
|
361
|
+
long
|
|
362
|
+
oj_check_circular(VALUE obj, Out out) {
|
|
363
|
+
slot_t id = 0;
|
|
364
|
+
slot_t *slot;
|
|
365
|
+
|
|
366
|
+
if (Yes == out->opts->circular) {
|
|
367
|
+
if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
|
|
368
|
+
out->circ_cnt++;
|
|
369
|
+
id = out->circ_cnt;
|
|
370
|
+
*slot = id;
|
|
371
|
+
} else {
|
|
372
|
+
if (ObjectMode == out->opts->mode) {
|
|
373
|
+
assure_size(out, 18);
|
|
374
|
+
*out->cur++ = '"';
|
|
375
|
+
*out->cur++ = '^';
|
|
376
|
+
*out->cur++ = 'r';
|
|
377
|
+
dump_ulong(id, out);
|
|
378
|
+
*out->cur++ = '"';
|
|
379
|
+
}
|
|
380
|
+
return -1;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return (long)id;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
void
|
|
387
|
+
oj_dump_time(VALUE obj, Out out, int withZone) {
|
|
388
|
+
char buf[64];
|
|
389
|
+
char *b = buf + sizeof(buf) - 1;
|
|
390
|
+
long size;
|
|
391
|
+
char *dot;
|
|
392
|
+
int neg = 0;
|
|
393
|
+
long one = 1000000000;
|
|
394
|
+
long long sec;
|
|
395
|
+
long long nsec;
|
|
396
|
+
|
|
397
|
+
#ifdef HAVE_RB_TIME_TIMESPEC
|
|
398
|
+
// rb_time_timespec as well as rb_time_timeeval have a bug that causes an
|
|
399
|
+
// exception to be raised if a time is before 1970 on 32 bit systems so
|
|
400
|
+
// check the timespec size and use the ruby calls if a 32 bit system.
|
|
401
|
+
if (16 <= sizeof(struct timespec)) {
|
|
402
|
+
struct timespec ts = rb_time_timespec(obj);
|
|
403
|
+
|
|
404
|
+
sec = (long long)ts.tv_sec;
|
|
405
|
+
nsec = ts.tv_nsec;
|
|
406
|
+
} else {
|
|
407
|
+
sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
408
|
+
nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
409
|
+
}
|
|
410
|
+
#else
|
|
411
|
+
sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
412
|
+
nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
413
|
+
#endif
|
|
414
|
+
|
|
415
|
+
*b-- = '\0';
|
|
416
|
+
if (withZone) {
|
|
417
|
+
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
|
418
|
+
int zneg = (0 > tzsecs);
|
|
419
|
+
|
|
420
|
+
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
|
421
|
+
tzsecs = 86400;
|
|
422
|
+
}
|
|
423
|
+
if (zneg) {
|
|
424
|
+
tzsecs = -tzsecs;
|
|
425
|
+
}
|
|
426
|
+
if (0 == tzsecs) {
|
|
427
|
+
*b-- = '0';
|
|
428
|
+
} else {
|
|
429
|
+
for (; 0 < tzsecs; b--, tzsecs /= 10) {
|
|
430
|
+
*b = '0' + (tzsecs % 10);
|
|
431
|
+
}
|
|
432
|
+
if (zneg) {
|
|
433
|
+
*b-- = '-';
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
*b-- = 'e';
|
|
437
|
+
}
|
|
438
|
+
if (0 > sec) {
|
|
439
|
+
neg = 1;
|
|
440
|
+
sec = -sec;
|
|
441
|
+
if (0 < nsec) {
|
|
442
|
+
nsec = 1000000000 - nsec;
|
|
443
|
+
sec--;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
dot = b - 9;
|
|
447
|
+
if (0 < out->opts->sec_prec) {
|
|
448
|
+
if (9 > out->opts->sec_prec) {
|
|
449
|
+
int i;
|
|
450
|
+
|
|
451
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
|
452
|
+
dot++;
|
|
453
|
+
nsec = (nsec + 5) / 10;
|
|
454
|
+
one /= 10;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (one <= nsec) {
|
|
458
|
+
nsec -= one;
|
|
459
|
+
sec++;
|
|
460
|
+
}
|
|
461
|
+
for (; dot < b; b--, nsec /= 10) {
|
|
462
|
+
*b = '0' + (nsec % 10);
|
|
463
|
+
}
|
|
464
|
+
*b-- = '.';
|
|
465
|
+
}
|
|
466
|
+
if (0 == sec) {
|
|
467
|
+
*b-- = '0';
|
|
468
|
+
} else {
|
|
469
|
+
for (; 0 < sec; b--, sec /= 10) {
|
|
470
|
+
*b = '0' + (sec % 10);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (neg) {
|
|
474
|
+
*b-- = '-';
|
|
475
|
+
}
|
|
476
|
+
b++;
|
|
477
|
+
size = sizeof(buf) - (b - buf) - 1;
|
|
478
|
+
assure_size(out, size);
|
|
479
|
+
memcpy(out->cur, b, size);
|
|
480
|
+
out->cur += size;
|
|
481
|
+
*out->cur = '\0';
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
void
|
|
485
|
+
oj_dump_ruby_time(VALUE obj, Out out) {
|
|
486
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
487
|
+
|
|
488
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
void
|
|
492
|
+
oj_dump_xml_time(VALUE obj, Out out) {
|
|
493
|
+
char buf[64];
|
|
494
|
+
struct _timeInfo ti;
|
|
495
|
+
long one = 1000000000;
|
|
496
|
+
int64_t sec;
|
|
497
|
+
long long nsec;
|
|
498
|
+
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
|
499
|
+
int tzhour, tzmin;
|
|
500
|
+
char tzsign = '+';
|
|
501
|
+
|
|
502
|
+
#ifdef HAVE_RB_TIME_TIMESPEC
|
|
503
|
+
if (16 <= sizeof(struct timespec)) {
|
|
504
|
+
struct timespec ts = rb_time_timespec(obj);
|
|
505
|
+
|
|
506
|
+
sec = ts.tv_sec;
|
|
507
|
+
nsec = ts.tv_nsec;
|
|
508
|
+
} else {
|
|
509
|
+
sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
510
|
+
nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
511
|
+
}
|
|
512
|
+
#else
|
|
513
|
+
sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
514
|
+
nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
515
|
+
#endif
|
|
516
|
+
|
|
517
|
+
assure_size(out, 36);
|
|
518
|
+
if (9 > out->opts->sec_prec) {
|
|
519
|
+
int i;
|
|
520
|
+
|
|
521
|
+
// This is pretty lame but to be compatible with rails and active
|
|
522
|
+
// support rounding is not done but instead a floor is done when
|
|
523
|
+
// second precision is 3 just to be like rails. sigh.
|
|
524
|
+
if (3 == out->opts->sec_prec) {
|
|
525
|
+
nsec /= 1000000;
|
|
526
|
+
one = 1000;
|
|
527
|
+
} else {
|
|
528
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
|
529
|
+
nsec = (nsec + 5) / 10;
|
|
530
|
+
one /= 10;
|
|
531
|
+
}
|
|
532
|
+
if (one <= nsec) {
|
|
533
|
+
nsec -= one;
|
|
534
|
+
sec++;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
// 2012-01-05T23:58:07.123456000+09:00
|
|
539
|
+
//tm = localtime(&sec);
|
|
540
|
+
sec += tzsecs;
|
|
541
|
+
sec_as_time((int64_t)sec, &ti);
|
|
542
|
+
if (0 > tzsecs) {
|
|
543
|
+
tzsign = '-';
|
|
544
|
+
tzhour = (int)(tzsecs / -3600);
|
|
545
|
+
tzmin = (int)(tzsecs / -60) - (tzhour * 60);
|
|
546
|
+
} else {
|
|
547
|
+
tzhour = (int)(tzsecs / 3600);
|
|
548
|
+
tzmin = (int)(tzsecs / 60) - (tzhour * 60);
|
|
549
|
+
}
|
|
550
|
+
if (0 == nsec || 0 == out->opts->sec_prec) {
|
|
551
|
+
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
|
552
|
+
sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
|
|
553
|
+
oj_dump_cstr(buf, 20, 0, 0, out);
|
|
554
|
+
} else {
|
|
555
|
+
sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec,
|
|
556
|
+
tzsign, tzhour, tzmin);
|
|
557
|
+
oj_dump_cstr(buf, 25, 0, 0, out);
|
|
558
|
+
}
|
|
559
|
+
} else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
|
560
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
|
|
561
|
+
int len = 30;
|
|
562
|
+
|
|
563
|
+
if (9 > out->opts->sec_prec) {
|
|
564
|
+
format[32] = '0' + out->opts->sec_prec;
|
|
565
|
+
len -= 9 - out->opts->sec_prec;
|
|
566
|
+
}
|
|
567
|
+
sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
|
|
568
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
|
569
|
+
} else {
|
|
570
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
|
|
571
|
+
int len = 35;
|
|
572
|
+
|
|
573
|
+
if (9 > out->opts->sec_prec) {
|
|
574
|
+
format[32] = '0' + out->opts->sec_prec;
|
|
575
|
+
len -= 9 - out->opts->sec_prec;
|
|
576
|
+
}
|
|
577
|
+
sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
|
|
578
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
void
|
|
583
|
+
oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
|
|
584
|
+
oj_dump_obj_to_json_using_params(obj, copts, out, 0, 0);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
void
|
|
588
|
+
oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
|
|
589
|
+
if (0 == out->buf) {
|
|
590
|
+
out->buf = ALLOC_N(char, 4096);
|
|
591
|
+
out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
|
|
592
|
+
out->allocated = true;
|
|
593
|
+
}
|
|
594
|
+
out->cur = out->buf;
|
|
595
|
+
out->circ_cnt = 0;
|
|
596
|
+
out->opts = copts;
|
|
597
|
+
out->hash_cnt = 0;
|
|
598
|
+
out->indent = copts->indent;
|
|
599
|
+
out->argc = argc;
|
|
600
|
+
out->argv = argv;
|
|
601
|
+
out->ropts = NULL;
|
|
602
|
+
if (Yes == copts->circular) {
|
|
603
|
+
oj_cache8_new(&out->circ_cache);
|
|
604
|
+
}
|
|
605
|
+
switch (copts->mode) {
|
|
606
|
+
case StrictMode: oj_dump_strict_val(obj, 0, out); break;
|
|
607
|
+
case NullMode: oj_dump_null_val(obj, 0, out); break;
|
|
608
|
+
case ObjectMode: oj_dump_obj_val(obj, 0, out); break;
|
|
609
|
+
case CompatMode: oj_dump_compat_val(obj, 0, out, Yes == copts->to_json); break;
|
|
610
|
+
case RailsMode: oj_dump_rails_val(obj, 0, out); break;
|
|
611
|
+
case CustomMode: oj_dump_custom_val(obj, 0, out, true); break;
|
|
612
|
+
case WabMode: oj_dump_wab_val(obj, 0, out); break;
|
|
613
|
+
default: oj_dump_custom_val(obj, 0, out, true); break;
|
|
614
|
+
}
|
|
615
|
+
if (0 < out->indent) {
|
|
616
|
+
switch (*(out->cur - 1)) {
|
|
617
|
+
case ']':
|
|
618
|
+
case '}':
|
|
619
|
+
assure_size(out, 1);
|
|
620
|
+
*out->cur++ = '\n';
|
|
621
|
+
default:
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
*out->cur = '\0';
|
|
626
|
+
if (Yes == copts->circular) {
|
|
627
|
+
oj_cache8_delete(out->circ_cache);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
void
|
|
632
|
+
oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
|
|
633
|
+
char buf[4096];
|
|
634
|
+
struct _out out;
|
|
635
|
+
size_t size;
|
|
636
|
+
FILE *f;
|
|
637
|
+
int ok;
|
|
638
|
+
|
|
639
|
+
out.buf = buf;
|
|
640
|
+
out.end = buf + sizeof(buf) - BUFFER_EXTRA;
|
|
641
|
+
out.allocated = false;
|
|
642
|
+
out.omit_nil = copts->dump_opts.omit_nil;
|
|
643
|
+
oj_dump_obj_to_json(obj, copts, &out);
|
|
644
|
+
size = out.cur - out.buf;
|
|
645
|
+
if (0 == (f = fopen(path, "w"))) {
|
|
646
|
+
if (out.allocated) {
|
|
647
|
+
xfree(out.buf);
|
|
648
|
+
}
|
|
649
|
+
rb_raise(rb_eIOError, "%s", strerror(errno));
|
|
650
|
+
}
|
|
651
|
+
ok = (size == fwrite(out.buf, 1, size, f));
|
|
652
|
+
if (out.allocated) {
|
|
653
|
+
xfree(out.buf);
|
|
654
|
+
}
|
|
655
|
+
fclose(f);
|
|
656
|
+
if (!ok) {
|
|
657
|
+
int err = ferror(f);
|
|
658
|
+
|
|
659
|
+
rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
void
|
|
664
|
+
oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
|
|
665
|
+
char buf[4096];
|
|
666
|
+
struct _out out;
|
|
667
|
+
ssize_t size;
|
|
668
|
+
VALUE clas = rb_obj_class(stream);
|
|
669
|
+
#if !IS_WINDOWS
|
|
670
|
+
int fd;
|
|
671
|
+
VALUE s;
|
|
672
|
+
#endif
|
|
673
|
+
|
|
674
|
+
out.buf = buf;
|
|
675
|
+
out.end = buf + sizeof(buf) - BUFFER_EXTRA;
|
|
676
|
+
out.allocated = false;
|
|
677
|
+
out.omit_nil = copts->dump_opts.omit_nil;
|
|
678
|
+
oj_dump_obj_to_json(obj, copts, &out);
|
|
679
|
+
size = out.cur - out.buf;
|
|
680
|
+
if (oj_stringio_class == clas) {
|
|
681
|
+
rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
|
|
682
|
+
#if !IS_WINDOWS
|
|
683
|
+
} else if (rb_respond_to(stream, oj_fileno_id) &&
|
|
684
|
+
Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
|
|
685
|
+
0 != (fd = FIX2INT(s))) {
|
|
686
|
+
if (size != write(fd, out.buf, size)) {
|
|
687
|
+
if (out.allocated) {
|
|
688
|
+
xfree(out.buf);
|
|
689
|
+
}
|
|
690
|
+
rb_raise(rb_eIOError, "Write failed. [%d:%s]", errno, strerror(errno));
|
|
691
|
+
}
|
|
692
|
+
#endif
|
|
693
|
+
} else if (rb_respond_to(stream, oj_write_id)) {
|
|
694
|
+
rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
|
|
695
|
+
} else {
|
|
696
|
+
if (out.allocated) {
|
|
697
|
+
xfree(out.buf);
|
|
698
|
+
}
|
|
699
|
+
rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
|
|
700
|
+
}
|
|
701
|
+
if (out.allocated) {
|
|
702
|
+
xfree(out.buf);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
void
|
|
707
|
+
oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
|
|
708
|
+
rb_encoding *enc = rb_to_encoding(rb_obj_encoding(obj));
|
|
709
|
+
|
|
710
|
+
if (rb_utf8_encoding() != enc) {
|
|
711
|
+
obj = rb_str_conv_enc(obj, enc, rb_utf8_encoding());
|
|
712
|
+
}
|
|
713
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&obj), (int)RSTRING_LEN(obj), 0, 0, out);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
void
|
|
717
|
+
oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
|
|
718
|
+
// This causes a memory leak in 2.5.1. Maybe in other versions as well.
|
|
719
|
+
//const char *sym = rb_id2name(SYM2ID(obj));
|
|
720
|
+
|
|
721
|
+
volatile VALUE s = rb_sym_to_s(obj);
|
|
722
|
+
|
|
723
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&s), (int)RSTRING_LEN(s), 0, 0, out);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
static void
|
|
727
|
+
debug_raise(const char *orig, size_t cnt, int line) {
|
|
728
|
+
char buf[1024];
|
|
729
|
+
char *b = buf;
|
|
730
|
+
const char *s = orig;
|
|
731
|
+
const char *s_end = s + cnt;
|
|
732
|
+
|
|
733
|
+
if (32 < s_end - s) {
|
|
734
|
+
s_end = s + 32;
|
|
735
|
+
}
|
|
736
|
+
for (; s < s_end; s++) {
|
|
737
|
+
b += sprintf(b, " %02x", *s);
|
|
738
|
+
}
|
|
739
|
+
*b = '\0';
|
|
740
|
+
rb_raise(oj_json_generator_error_class, "Partial character in string. %s @ %d", buf, line);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
void
|
|
744
|
+
oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
|
|
745
|
+
size_t size;
|
|
746
|
+
char *cmap;
|
|
747
|
+
const char *orig = str;
|
|
748
|
+
|
|
749
|
+
switch (out->opts->escape_mode) {
|
|
750
|
+
case NLEsc:
|
|
751
|
+
cmap = newline_friendly_chars;
|
|
752
|
+
size = newline_friendly_size((uint8_t*)str, cnt);
|
|
753
|
+
break;
|
|
754
|
+
case ASCIIEsc:
|
|
755
|
+
cmap = ascii_friendly_chars;
|
|
756
|
+
size = ascii_friendly_size((uint8_t*)str, cnt);
|
|
757
|
+
break;
|
|
758
|
+
case XSSEsc:
|
|
759
|
+
cmap = xss_friendly_chars;
|
|
760
|
+
size = xss_friendly_size((uint8_t*)str, cnt);
|
|
761
|
+
break;
|
|
762
|
+
case JXEsc:
|
|
763
|
+
cmap = hixss_friendly_chars;
|
|
764
|
+
size = hixss_friendly_size((uint8_t*)str, cnt);
|
|
765
|
+
break;
|
|
766
|
+
case RailsXEsc:
|
|
767
|
+
cmap = rails_xss_friendly_chars;
|
|
768
|
+
size = rails_xss_friendly_size((uint8_t*)str, cnt);
|
|
769
|
+
break;
|
|
770
|
+
case RailsEsc:
|
|
771
|
+
cmap = rails_friendly_chars;
|
|
772
|
+
size = rails_friendly_size((uint8_t*)str, cnt);
|
|
773
|
+
break;
|
|
774
|
+
case JSONEsc:
|
|
775
|
+
default:
|
|
776
|
+
cmap = hibit_friendly_chars;
|
|
777
|
+
size = hibit_friendly_size((uint8_t*)str, cnt);
|
|
778
|
+
}
|
|
779
|
+
assure_size(out, size + BUFFER_EXTRA);
|
|
780
|
+
*out->cur++ = '"';
|
|
781
|
+
|
|
782
|
+
if (escape1) {
|
|
783
|
+
*out->cur++ = '\\';
|
|
784
|
+
*out->cur++ = 'u';
|
|
785
|
+
*out->cur++ = '0';
|
|
786
|
+
*out->cur++ = '0';
|
|
787
|
+
dump_hex((uint8_t)*str, out);
|
|
788
|
+
cnt--;
|
|
789
|
+
size--;
|
|
790
|
+
str++;
|
|
791
|
+
is_sym = 0; // just to make sure
|
|
792
|
+
}
|
|
793
|
+
if (cnt == size) {
|
|
794
|
+
if (is_sym) {
|
|
795
|
+
*out->cur++ = ':';
|
|
796
|
+
}
|
|
797
|
+
for (; '\0' != *str; str++) {
|
|
798
|
+
*out->cur++ = *str;
|
|
799
|
+
}
|
|
800
|
+
*out->cur++ = '"';
|
|
801
|
+
} else {
|
|
802
|
+
const char *end = str + cnt;
|
|
803
|
+
const char *check_start = str;
|
|
804
|
+
|
|
805
|
+
if (is_sym) {
|
|
806
|
+
*out->cur++ = ':';
|
|
807
|
+
}
|
|
808
|
+
for (; str < end; str++) {
|
|
809
|
+
switch (cmap[(uint8_t)*str]) {
|
|
810
|
+
case '1':
|
|
811
|
+
if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && check_start <= str) {
|
|
812
|
+
if (0 != (0x80 & (uint8_t)*str)) {
|
|
813
|
+
if (0xC0 == (0xC0 & (uint8_t)*str)) {
|
|
814
|
+
check_start = check_unicode(str, end, orig);
|
|
815
|
+
} else {
|
|
816
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
*out->cur++ = *str;
|
|
821
|
+
break;
|
|
822
|
+
case '2':
|
|
823
|
+
*out->cur++ = '\\';
|
|
824
|
+
switch (*str) {
|
|
825
|
+
case '\\': *out->cur++ = '\\'; break;
|
|
826
|
+
case '\b': *out->cur++ = 'b'; break;
|
|
827
|
+
case '\t': *out->cur++ = 't'; break;
|
|
828
|
+
case '\n': *out->cur++ = 'n'; break;
|
|
829
|
+
case '\f': *out->cur++ = 'f'; break;
|
|
830
|
+
case '\r': *out->cur++ = 'r'; break;
|
|
831
|
+
default: *out->cur++ = *str; break;
|
|
832
|
+
}
|
|
833
|
+
break;
|
|
834
|
+
case '3': // Unicode
|
|
835
|
+
if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
|
|
836
|
+
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
|
837
|
+
str = dump_unicode(str, end, out, orig);
|
|
838
|
+
} else {
|
|
839
|
+
check_start = check_unicode(str, end, orig);
|
|
840
|
+
*out->cur++ = *str;
|
|
841
|
+
}
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
str = dump_unicode(str, end, out, orig);
|
|
845
|
+
break;
|
|
846
|
+
case '6': // control characters
|
|
847
|
+
if (*(uint8_t*)str < 0x80) {
|
|
848
|
+
*out->cur++ = '\\';
|
|
849
|
+
*out->cur++ = 'u';
|
|
850
|
+
*out->cur++ = '0';
|
|
851
|
+
*out->cur++ = '0';
|
|
852
|
+
dump_hex((uint8_t)*str, out);
|
|
853
|
+
} else {
|
|
854
|
+
if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
|
|
855
|
+
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
|
856
|
+
str = dump_unicode(str, end, out, orig);
|
|
857
|
+
} else {
|
|
858
|
+
check_start = check_unicode(str, end, orig);
|
|
859
|
+
*out->cur++ = *str;
|
|
860
|
+
}
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
str = dump_unicode(str, end, out, orig);
|
|
864
|
+
}
|
|
865
|
+
break;
|
|
866
|
+
default:
|
|
867
|
+
break; // ignore, should never happen if the table is correct
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
*out->cur++ = '"';
|
|
871
|
+
}
|
|
872
|
+
if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
|
|
873
|
+
uint8_t c = (uint8_t)*(str - 1);
|
|
874
|
+
int i;
|
|
875
|
+
int scnt = (int)(str - orig);
|
|
876
|
+
|
|
877
|
+
// Last utf-8 characters must be 0x10xxxxxx. The start must be
|
|
878
|
+
// 0x110xxxxx for 2 characters, 0x1110xxxx for 3, and 0x11110xxx for
|
|
879
|
+
// 4.
|
|
880
|
+
if (0 != (0x40 & c)) {
|
|
881
|
+
debug_raise(orig, cnt, __LINE__);
|
|
882
|
+
}
|
|
883
|
+
for (i = 1; i < (int)scnt && i < 4; i++) {
|
|
884
|
+
c = str[-1 - i];
|
|
885
|
+
if (0x80 != (0xC0 & c)) {
|
|
886
|
+
switch (i) {
|
|
887
|
+
case 1:
|
|
888
|
+
if (0xC0 != (0xE0 & c)) {
|
|
889
|
+
debug_raise(orig, cnt, __LINE__);
|
|
890
|
+
}
|
|
891
|
+
break;
|
|
892
|
+
case 2:
|
|
893
|
+
if (0xE0 != (0xF0 & c)) {
|
|
894
|
+
debug_raise(orig, cnt, __LINE__);
|
|
895
|
+
}
|
|
896
|
+
break;
|
|
897
|
+
case 3:
|
|
898
|
+
if (0xF0 != (0xF8 & c)) {
|
|
899
|
+
debug_raise(orig, cnt, __LINE__);
|
|
900
|
+
}
|
|
901
|
+
break;
|
|
902
|
+
default: // can't get here
|
|
903
|
+
break;
|
|
904
|
+
}
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (i == (int)scnt || 4 <= i) {
|
|
909
|
+
debug_raise(orig, cnt, __LINE__);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
*out->cur = '\0';
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
void
|
|
916
|
+
oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
|
|
917
|
+
const char *s = rb_class2name(obj);
|
|
918
|
+
|
|
919
|
+
oj_dump_cstr(s, strlen(s), 0, 0, out);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
void
|
|
923
|
+
oj_dump_obj_to_s(VALUE obj, Out out) {
|
|
924
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
925
|
+
|
|
926
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
void
|
|
930
|
+
oj_dump_raw(const char *str, size_t cnt, Out out) {
|
|
931
|
+
assure_size(out, cnt + 10);
|
|
932
|
+
memcpy(out->cur, str, cnt);
|
|
933
|
+
out->cur += cnt;
|
|
934
|
+
*out->cur = '\0';
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
void
|
|
938
|
+
oj_grow_out(Out out, size_t len) {
|
|
939
|
+
size_t size = out->end - out->buf;
|
|
940
|
+
long pos = out->cur - out->buf;
|
|
941
|
+
char *buf = out->buf;
|
|
942
|
+
|
|
943
|
+
size *= 2;
|
|
944
|
+
if (size <= len * 2 + pos) {
|
|
945
|
+
size += len;
|
|
946
|
+
}
|
|
947
|
+
if (out->allocated) {
|
|
948
|
+
REALLOC_N(buf, char, (size + BUFFER_EXTRA));
|
|
949
|
+
} else {
|
|
950
|
+
buf = ALLOC_N(char, (size + BUFFER_EXTRA));
|
|
951
|
+
out->allocated = true;
|
|
952
|
+
memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
|
|
953
|
+
}
|
|
954
|
+
if (0 == buf) {
|
|
955
|
+
rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]", ENOSPC, strerror(ENOSPC));
|
|
956
|
+
}
|
|
957
|
+
out->buf = buf;
|
|
958
|
+
out->end = buf + size;
|
|
959
|
+
out->cur = out->buf + pos;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
void
|
|
963
|
+
oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
|
|
964
|
+
assure_size(out, 4);
|
|
965
|
+
*out->cur++ = 'n';
|
|
966
|
+
*out->cur++ = 'u';
|
|
967
|
+
*out->cur++ = 'l';
|
|
968
|
+
*out->cur++ = 'l';
|
|
969
|
+
*out->cur = '\0';
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
void
|
|
973
|
+
oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
|
|
974
|
+
assure_size(out, 4);
|
|
975
|
+
*out->cur++ = 't';
|
|
976
|
+
*out->cur++ = 'r';
|
|
977
|
+
*out->cur++ = 'u';
|
|
978
|
+
*out->cur++ = 'e';
|
|
979
|
+
*out->cur = '\0';
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
void
|
|
983
|
+
oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
|
|
984
|
+
assure_size(out, 5);
|
|
985
|
+
*out->cur++ = 'f';
|
|
986
|
+
*out->cur++ = 'a';
|
|
987
|
+
*out->cur++ = 'l';
|
|
988
|
+
*out->cur++ = 's';
|
|
989
|
+
*out->cur++ = 'e';
|
|
990
|
+
*out->cur = '\0';
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
void
|
|
994
|
+
oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
995
|
+
char buf[32];
|
|
996
|
+
char *b = buf + sizeof(buf) - 1;
|
|
997
|
+
long long num = rb_num2ll(obj);
|
|
998
|
+
int neg = 0;
|
|
999
|
+
bool dump_as_string = false;
|
|
1000
|
+
|
|
1001
|
+
if (out->opts->integer_range_max != 0 && out->opts->integer_range_min != 0 &&
|
|
1002
|
+
(out->opts->integer_range_max < num || out->opts->integer_range_min > num)) {
|
|
1003
|
+
dump_as_string = true;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (0 > num) {
|
|
1007
|
+
neg = 1;
|
|
1008
|
+
num = -num;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
*b-- = '\0';
|
|
1012
|
+
|
|
1013
|
+
if (dump_as_string) {
|
|
1014
|
+
*b-- = '"';
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
if (0 < num) {
|
|
1018
|
+
for (; 0 < num; num /= 10, b--) {
|
|
1019
|
+
*b = (num % 10) + '0';
|
|
1020
|
+
}
|
|
1021
|
+
if (neg) {
|
|
1022
|
+
*b = '-';
|
|
1023
|
+
} else {
|
|
1024
|
+
b++;
|
|
1025
|
+
}
|
|
1026
|
+
} else {
|
|
1027
|
+
*b = '0';
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
if (dump_as_string) {
|
|
1031
|
+
*--b = '"';
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
assure_size(out, (sizeof(buf) - (b - buf)));
|
|
1035
|
+
for (; '\0' != *b; b++) {
|
|
1036
|
+
*out->cur++ = *b;
|
|
1037
|
+
}
|
|
1038
|
+
*out->cur = '\0';
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
void
|
|
1042
|
+
oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1043
|
+
volatile VALUE rs = rb_big2str(obj, 10);
|
|
1044
|
+
int cnt = (int)RSTRING_LEN(rs);
|
|
1045
|
+
bool dump_as_string = false;
|
|
1046
|
+
|
|
1047
|
+
if (out->opts->integer_range_max != 0 || out->opts->integer_range_min != 0) { // Bignum cannot be inside of Fixnum range
|
|
1048
|
+
dump_as_string = true;
|
|
1049
|
+
assure_size(out, cnt + 2);
|
|
1050
|
+
*out->cur++ = '"';
|
|
1051
|
+
} else {
|
|
1052
|
+
assure_size(out, cnt);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
|
|
1056
|
+
out->cur += cnt;
|
|
1057
|
+
|
|
1058
|
+
if(dump_as_string) {
|
|
1059
|
+
*out->cur++ = '"';
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
*out->cur = '\0';
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Removed dependencies on math due to problems with CentOS 5.4.
|
|
1066
|
+
void
|
|
1067
|
+
oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1068
|
+
char buf[64];
|
|
1069
|
+
char *b;
|
|
1070
|
+
double d = rb_num2dbl(obj);
|
|
1071
|
+
int cnt = 0;
|
|
1072
|
+
|
|
1073
|
+
if (0.0 == d) {
|
|
1074
|
+
b = buf;
|
|
1075
|
+
*b++ = '0';
|
|
1076
|
+
*b++ = '.';
|
|
1077
|
+
*b++ = '0';
|
|
1078
|
+
*b++ = '\0';
|
|
1079
|
+
cnt = 3;
|
|
1080
|
+
} else if (OJ_INFINITY == d) {
|
|
1081
|
+
if (ObjectMode == out->opts->mode) {
|
|
1082
|
+
strcpy(buf, inf_val);
|
|
1083
|
+
cnt = sizeof(inf_val) - 1;
|
|
1084
|
+
} else {
|
|
1085
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
|
1086
|
+
|
|
1087
|
+
if (AutoNan == nd) {
|
|
1088
|
+
switch (out->opts->mode) {
|
|
1089
|
+
case CompatMode: nd = WordNan; break;
|
|
1090
|
+
case StrictMode: nd = RaiseNan; break;
|
|
1091
|
+
case NullMode: nd = NullNan; break;
|
|
1092
|
+
case CustomMode: nd = NullNan; break;
|
|
1093
|
+
default: break;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
switch (nd) {
|
|
1097
|
+
case RaiseNan:
|
|
1098
|
+
raise_strict(obj);
|
|
1099
|
+
break;
|
|
1100
|
+
case WordNan:
|
|
1101
|
+
strcpy(buf, "Infinity");
|
|
1102
|
+
cnt = 8;
|
|
1103
|
+
break;
|
|
1104
|
+
case NullNan:
|
|
1105
|
+
strcpy(buf, "null");
|
|
1106
|
+
cnt = 4;
|
|
1107
|
+
break;
|
|
1108
|
+
case HugeNan:
|
|
1109
|
+
default:
|
|
1110
|
+
strcpy(buf, inf_val);
|
|
1111
|
+
cnt = sizeof(inf_val) - 1;
|
|
1112
|
+
break;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
} else if (-OJ_INFINITY == d) {
|
|
1116
|
+
if (ObjectMode == out->opts->mode) {
|
|
1117
|
+
strcpy(buf, ninf_val);
|
|
1118
|
+
cnt = sizeof(ninf_val) - 1;
|
|
1119
|
+
} else {
|
|
1120
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
|
1121
|
+
|
|
1122
|
+
if (AutoNan == nd) {
|
|
1123
|
+
switch (out->opts->mode) {
|
|
1124
|
+
case CompatMode: nd = WordNan; break;
|
|
1125
|
+
case StrictMode: nd = RaiseNan; break;
|
|
1126
|
+
case NullMode: nd = NullNan; break;
|
|
1127
|
+
default: break;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
switch (nd) {
|
|
1131
|
+
case RaiseNan:
|
|
1132
|
+
raise_strict(obj);
|
|
1133
|
+
break;
|
|
1134
|
+
case WordNan:
|
|
1135
|
+
strcpy(buf, "-Infinity");
|
|
1136
|
+
cnt = 9;
|
|
1137
|
+
break;
|
|
1138
|
+
case NullNan:
|
|
1139
|
+
strcpy(buf, "null");
|
|
1140
|
+
cnt = 4;
|
|
1141
|
+
break;
|
|
1142
|
+
case HugeNan:
|
|
1143
|
+
default:
|
|
1144
|
+
strcpy(buf, ninf_val);
|
|
1145
|
+
cnt = sizeof(ninf_val) - 1;
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
} else if (isnan(d)) {
|
|
1150
|
+
if (ObjectMode == out->opts->mode) {
|
|
1151
|
+
strcpy(buf, nan_val);
|
|
1152
|
+
cnt = sizeof(ninf_val) - 1;
|
|
1153
|
+
} else {
|
|
1154
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
|
1155
|
+
|
|
1156
|
+
if (AutoNan == nd) {
|
|
1157
|
+
switch (out->opts->mode) {
|
|
1158
|
+
case ObjectMode: nd = HugeNan; break;
|
|
1159
|
+
case StrictMode: nd = RaiseNan; break;
|
|
1160
|
+
case NullMode: nd = NullNan; break;
|
|
1161
|
+
default: break;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
switch (nd) {
|
|
1165
|
+
case RaiseNan:
|
|
1166
|
+
raise_strict(obj);
|
|
1167
|
+
break;
|
|
1168
|
+
case WordNan:
|
|
1169
|
+
strcpy(buf, "NaN");
|
|
1170
|
+
cnt = 3;
|
|
1171
|
+
break;
|
|
1172
|
+
case NullNan:
|
|
1173
|
+
strcpy(buf, "null");
|
|
1174
|
+
cnt = 4;
|
|
1175
|
+
break;
|
|
1176
|
+
case HugeNan:
|
|
1177
|
+
default:
|
|
1178
|
+
strcpy(buf, nan_val);
|
|
1179
|
+
cnt = sizeof(nan_val) - 1;
|
|
1180
|
+
break;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
} else if (d == (double)(long long int)d) {
|
|
1184
|
+
cnt = snprintf(buf, sizeof(buf), "%.1f", d);
|
|
1185
|
+
} else if (0 == out->opts->float_prec) {
|
|
1186
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
1187
|
+
|
|
1188
|
+
cnt = (int)RSTRING_LEN(rstr);
|
|
1189
|
+
if ((int)sizeof(buf) <= cnt) {
|
|
1190
|
+
cnt = sizeof(buf) - 1;
|
|
1191
|
+
}
|
|
1192
|
+
strncpy(buf, rb_string_value_ptr((VALUE*)&rstr), cnt);
|
|
1193
|
+
buf[cnt] = '\0';
|
|
1194
|
+
} else {
|
|
1195
|
+
cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
|
|
1196
|
+
}
|
|
1197
|
+
assure_size(out, cnt);
|
|
1198
|
+
for (b = buf; '\0' != *b; b++) {
|
|
1199
|
+
*out->cur++ = *b;
|
|
1200
|
+
}
|
|
1201
|
+
*out->cur = '\0';
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
int
|
|
1205
|
+
oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
|
|
1206
|
+
int cnt = snprintf(buf, blen, format, d);
|
|
1207
|
+
|
|
1208
|
+
// Round off issues at 16 significant digits so check for obvious ones of
|
|
1209
|
+
// 0001 and 9999.
|
|
1210
|
+
if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
|
|
1211
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
1212
|
+
|
|
1213
|
+
strcpy(buf, rb_string_value_ptr((VALUE*)&rstr));
|
|
1214
|
+
cnt = (int)RSTRING_LEN(rstr);
|
|
1215
|
+
}
|
|
1216
|
+
return cnt;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
bool
|
|
1220
|
+
oj_dump_ignore(Options opts, VALUE obj) {
|
|
1221
|
+
if (NULL != opts->ignore && (ObjectMode == opts->mode || CustomMode == opts->mode)) {
|
|
1222
|
+
VALUE *vp = opts->ignore;
|
|
1223
|
+
VALUE clas = rb_obj_class(obj);
|
|
1224
|
+
|
|
1225
|
+
for (; Qnil != *vp; vp++) {
|
|
1226
|
+
if (clas == *vp) {
|
|
1227
|
+
return true;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|