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/dump.c
CHANGED
@@ -1,118 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
* Redistribution and use in source and binary forms, with or without
|
6
|
-
* modification, are permitted provided that the following conditions are met:
|
7
|
-
*
|
8
|
-
* - Redistributions of source code must retain the above copyright notice, this
|
9
|
-
* list of conditions and the following disclaimer.
|
10
|
-
*
|
11
|
-
* - Redistributions in binary form must reproduce the above copyright notice,
|
12
|
-
* this list of conditions and the following disclaimer in the documentation
|
13
|
-
* and/or other materials provided with the distribution.
|
14
|
-
*
|
15
|
-
* - Neither the name of Peter Ohler nor the names of its contributors may be
|
16
|
-
* used to endorse or promote products derived from this software without
|
17
|
-
* specific prior written permission.
|
18
|
-
*
|
19
|
-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
-
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
23
|
-
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
-
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
25
|
-
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
26
|
-
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
27
|
-
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
-
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
-
*/
|
1
|
+
// Copyright (c) 2012, 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 "dump.h"
|
30
5
|
|
31
|
-
#include <stdlib.h>
|
32
6
|
#include <errno.h>
|
33
|
-
#
|
34
|
-
#include <
|
35
|
-
#endif
|
36
|
-
#include <time.h>
|
7
|
+
#include <math.h>
|
8
|
+
#include <stdint.h>
|
37
9
|
#include <stdio.h>
|
10
|
+
#include <stdlib.h>
|
38
11
|
#include <string.h>
|
39
|
-
#include <math.h>
|
40
12
|
#include <unistd.h>
|
41
|
-
#
|
13
|
+
#if !IS_WINDOWS
|
14
|
+
#include <poll.h>
|
15
|
+
#endif
|
42
16
|
|
43
|
-
#include "oj.h"
|
44
17
|
#include "cache8.h"
|
18
|
+
#include "mem.h"
|
45
19
|
#include "odd.h"
|
46
|
-
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#endif
|
20
|
+
#include "oj.h"
|
21
|
+
#include "trace.h"
|
22
|
+
#include "util.h"
|
50
23
|
|
51
24
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
52
|
-
#define OJ_INFINITY (1.0/0.0)
|
53
|
-
|
54
|
-
// Extra padding at end of buffer.
|
55
|
-
#define BUFFER_EXTRA 10
|
25
|
+
#define OJ_INFINITY (1.0 / 0.0)
|
56
26
|
|
57
27
|
#define MAX_DEPTH 1000
|
58
28
|
|
59
|
-
|
60
|
-
|
61
|
-
static
|
62
|
-
static void dump_val(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
|
63
|
-
static void dump_nil(Out out);
|
64
|
-
static void dump_true(Out out);
|
65
|
-
static void dump_false(Out out);
|
66
|
-
static void dump_fixnum(VALUE obj, Out out);
|
67
|
-
static void dump_bignum(VALUE obj, Out out);
|
68
|
-
static void dump_float(VALUE obj, Out out);
|
69
|
-
static void dump_raw(const char *str, size_t cnt, Out out);
|
70
|
-
static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
|
71
|
-
static void dump_hex(uint8_t c, Out out);
|
72
|
-
static void dump_str_comp(VALUE obj, Out out);
|
73
|
-
static void dump_str_obj(VALUE obj, VALUE clas, int depth, Out out);
|
74
|
-
static void dump_sym_comp(VALUE obj, Out out);
|
75
|
-
static void dump_sym_obj(VALUE obj, Out out);
|
76
|
-
static void dump_class_comp(VALUE obj, Out out);
|
77
|
-
static void dump_class_obj(VALUE obj, Out out);
|
78
|
-
static void dump_array(VALUE obj, VALUE clas, int depth, Out out);
|
79
|
-
static int hash_cb_strict(VALUE key, VALUE value, Out out);
|
80
|
-
static int hash_cb_compat(VALUE key, VALUE value, Out out);
|
81
|
-
static int hash_cb_object(VALUE key, VALUE value, Out out);
|
82
|
-
static void dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out);
|
83
|
-
static void dump_time(VALUE obj, Out out, int withZone);
|
84
|
-
static void dump_ruby_time(VALUE obj, Out out);
|
85
|
-
static void dump_xml_time(VALUE obj, Out out);
|
86
|
-
static void dump_data_strict(VALUE obj, Out out);
|
87
|
-
static void dump_data_null(VALUE obj, Out out);
|
88
|
-
static void dump_data_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
|
89
|
-
static void dump_data_obj(VALUE obj, int depth, Out out);
|
90
|
-
static void dump_obj_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
|
91
|
-
static void dump_obj_obj(VALUE obj, int depth, Out out);
|
92
|
-
static void dump_struct_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
|
93
|
-
static void dump_struct_obj(VALUE obj, int depth, Out out);
|
94
|
-
#if HAS_IVAR_HELPERS
|
95
|
-
static int dump_attr_cb(ID key, VALUE value, Out out);
|
96
|
-
#endif
|
97
|
-
static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
|
98
|
-
static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out);
|
29
|
+
static const char inf_val[] = INF_VAL;
|
30
|
+
static const char ninf_val[] = NINF_VAL;
|
31
|
+
static const char nan_val[] = NAN_VAL;
|
99
32
|
|
100
|
-
|
101
|
-
static size_t hibit_friendly_size(const uint8_t *str, size_t len);
|
102
|
-
static size_t xss_friendly_size(const uint8_t *str, size_t len);
|
103
|
-
static size_t ascii_friendly_size(const uint8_t *str, size_t len);
|
33
|
+
typedef unsigned long ulong;
|
104
34
|
|
105
|
-
static
|
106
|
-
static
|
107
|
-
static
|
108
|
-
static
|
109
|
-
static void dump_leaf_array(Leaf leaf, int depth, Out out);
|
110
|
-
static void dump_leaf_hash(Leaf leaf, int depth, Out out);
|
35
|
+
static size_t hibit_friendly_size(const uint8_t *str, size_t len);
|
36
|
+
static size_t slash_friendly_size(const uint8_t *str, size_t len);
|
37
|
+
static size_t xss_friendly_size(const uint8_t *str, size_t len);
|
38
|
+
static size_t ascii_friendly_size(const uint8_t *str, size_t len);
|
111
39
|
|
112
|
-
static const char
|
40
|
+
static const char hex_chars[17] = "0123456789abcdef";
|
113
41
|
|
114
42
|
// JSON standard except newlines are no escaped
|
115
|
-
static char
|
43
|
+
static char newline_friendly_chars[256] = "\
|
116
44
|
66666666221622666666666666666666\
|
117
45
|
11211111111111111111111111111111\
|
118
46
|
11111111111111111111111111112111\
|
@@ -123,7 +51,7 @@ static char newline_friendly_chars[256] = "\
|
|
123
51
|
11111111111111111111111111111111";
|
124
52
|
|
125
53
|
// JSON standard
|
126
|
-
static char
|
54
|
+
static char hibit_friendly_chars[256] = "\
|
127
55
|
66666666222622666666666666666666\
|
128
56
|
11211111111111111111111111111111\
|
129
57
|
11111111111111111111111111112111\
|
@@ -133,9 +61,20 @@ static char hibit_friendly_chars[256] = "\
|
|
133
61
|
11111111111111111111111111111111\
|
134
62
|
11111111111111111111111111111111";
|
135
63
|
|
64
|
+
// JSON standard but escape forward slashes `/`
|
65
|
+
static char slash_friendly_chars[256] = "\
|
66
|
+
66666666222622666666666666666666\
|
67
|
+
11211111111111121111111111111111\
|
68
|
+
11111111111111111111111111112111\
|
69
|
+
11111111111111111111111111111111\
|
70
|
+
11111111111111111111111111111111\
|
71
|
+
11111111111111111111111111111111\
|
72
|
+
11111111111111111111111111111111\
|
73
|
+
11111111111111111111111111111111";
|
74
|
+
|
136
75
|
// High bit set characters are always encoded as unicode. Worse case is 3
|
137
76
|
// bytes per character in the output. That makes this conservative.
|
138
|
-
static char
|
77
|
+
static char ascii_friendly_chars[256] = "\
|
139
78
|
66666666222622666666666666666666\
|
140
79
|
11211111111111111111111111111111\
|
141
80
|
11111111111111111111111111112111\
|
@@ -146,7 +85,7 @@ static char ascii_friendly_chars[256] = "\
|
|
146
85
|
33333333333333333333333333333333";
|
147
86
|
|
148
87
|
// XSS safe mode
|
149
|
-
static char
|
88
|
+
static char xss_friendly_chars[256] = "\
|
150
89
|
66666666222622666666666666666666\
|
151
90
|
11211161111111121111111111116161\
|
152
91
|
11111111111111111111111111112111\
|
@@ -156,2616 +95,1459 @@ static char xss_friendly_chars[256] = "\
|
|
156
95
|
33333333333333333333333333333333\
|
157
96
|
33333333333333333333333333333333";
|
158
97
|
|
159
|
-
|
160
|
-
|
161
|
-
|
98
|
+
// JSON XSS combo
|
99
|
+
static char hixss_friendly_chars[256] = "\
|
100
|
+
66666666222622666666666666666666\
|
101
|
+
11211111111111111111111111111111\
|
102
|
+
11111111111111111111111111112111\
|
103
|
+
11111111111111111111111111111111\
|
104
|
+
11111111111111111111111111111111\
|
105
|
+
11111111111111111111111111111111\
|
106
|
+
11111111111111111111111111111111\
|
107
|
+
11611111111111111111111111111111";
|
162
108
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
109
|
+
// Rails XSS combo
|
110
|
+
static char rails_xss_friendly_chars[256] = "\
|
111
|
+
66666666222622666666666666666666\
|
112
|
+
11211161111111111111111111116161\
|
113
|
+
11111111111111111111111111112111\
|
114
|
+
11111111111111111111111111111111\
|
115
|
+
11111111111111111111111111111111\
|
116
|
+
11111111111111111111111111111111\
|
117
|
+
11111111111111111111111111111111\
|
118
|
+
11611111111111111111111111111111";
|
168
119
|
|
169
|
-
|
170
|
-
|
171
|
-
|
120
|
+
// Rails HTML non-escape
|
121
|
+
static char rails_friendly_chars[256] = "\
|
122
|
+
66666666222622666666666666666666\
|
123
|
+
11211111111111111111111111111111\
|
124
|
+
11111111111111111111111111112111\
|
125
|
+
11111111111111111111111111111111\
|
126
|
+
11111111111111111111111111111111\
|
127
|
+
11111111111111111111111111111111\
|
128
|
+
11111111111111111111111111111111\
|
129
|
+
11111111111111111111111111111111";
|
172
130
|
|
173
|
-
|
174
|
-
|
175
|
-
}
|
176
|
-
return size - len * (size_t)'0';
|
131
|
+
static void raise_strict(VALUE obj) {
|
132
|
+
rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.", rb_class2name(rb_obj_class(obj)));
|
177
133
|
}
|
178
134
|
|
179
|
-
inline static size_t
|
180
|
-
|
181
|
-
size_t
|
135
|
+
inline static size_t calculate_string_size(const uint8_t *str, size_t len, const char *table) {
|
136
|
+
size_t size = 0;
|
137
|
+
size_t i = len;
|
182
138
|
|
183
|
-
for (;
|
184
|
-
|
139
|
+
for (; 3 < i; i -= 4) {
|
140
|
+
size += table[*str++];
|
141
|
+
size += table[*str++];
|
142
|
+
size += table[*str++];
|
143
|
+
size += table[*str++];
|
144
|
+
}
|
145
|
+
for (; 0 < i; i--) {
|
146
|
+
size += table[*str++];
|
185
147
|
}
|
186
148
|
return size - len * (size_t)'0';
|
187
149
|
}
|
188
150
|
|
189
|
-
inline static size_t
|
190
|
-
|
191
|
-
size_t size = 0;
|
192
|
-
|
193
|
-
for (; 0 < len; str++, len--) {
|
194
|
-
size += xss_friendly_chars[*str];
|
195
|
-
}
|
196
|
-
return size - len * (size_t)'0';
|
151
|
+
inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
|
152
|
+
return calculate_string_size(str, len, newline_friendly_chars);
|
197
153
|
}
|
198
154
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
}
|
155
|
+
#ifdef HAVE_SIMD_NEON
|
156
|
+
inline static uint8x16x4_t load_uint8x16_4(const unsigned char *table) {
|
157
|
+
uint8x16x4_t tab;
|
158
|
+
tab.val[0] = vld1q_u8(table);
|
159
|
+
tab.val[1] = vld1q_u8(table + 16);
|
160
|
+
tab.val[2] = vld1q_u8(table + 32);
|
161
|
+
tab.val[3] = vld1q_u8(table + 48);
|
162
|
+
return tab;
|
208
163
|
}
|
209
164
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
char *b = buf + sizeof(buf) - 1;
|
165
|
+
static uint8x16x4_t hibit_friendly_chars_neon[2];
|
166
|
+
static uint8x16x4_t rails_friendly_chars_neon[2];
|
167
|
+
static uint8x16x4_t rails_xss_friendly_chars_neon[4];
|
214
168
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
b++;
|
221
|
-
} else {
|
222
|
-
*b = '0';
|
223
|
-
}
|
224
|
-
for (; '\0' != *b; b++) {
|
225
|
-
*out->cur++ = *b;
|
226
|
-
}
|
227
|
-
*out->cur = '\0';
|
228
|
-
}
|
169
|
+
void initialize_neon(void) {
|
170
|
+
// We only need the first 128 bytes of the hibit friendly chars table. Everything above 127 is
|
171
|
+
// set to 1. If that ever changes, the code will need to be updated.
|
172
|
+
hibit_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)hibit_friendly_chars);
|
173
|
+
hibit_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)hibit_friendly_chars + 64);
|
229
174
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
175
|
+
// rails_friendly_chars is the same as hibit_friendly_chars. Only the first 128 bytes have values
|
176
|
+
// that are not '1'. If that ever changes, the code will need to be updated.
|
177
|
+
rails_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)rails_friendly_chars);
|
178
|
+
rails_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)rails_friendly_chars + 64);
|
179
|
+
|
180
|
+
rails_xss_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars);
|
181
|
+
rails_xss_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 64);
|
182
|
+
rails_xss_friendly_chars_neon[2] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 128);
|
183
|
+
rails_xss_friendly_chars_neon[3] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 192);
|
184
|
+
|
185
|
+
// All bytes should be 0 except for those that need more than 1 byte of output. This will allow the
|
186
|
+
// code to limit the lookups to the first 128 bytes (values 0 - 127). Bytes above 127 will result
|
187
|
+
// in 0 with the vqtbl4q_u8 instruction.
|
188
|
+
uint8x16_t one = vdupq_n_u8('1');
|
189
|
+
for (int i = 0; i < 2; i++) {
|
190
|
+
for (int j = 0; j < 4; j++) {
|
191
|
+
hibit_friendly_chars_neon[i].val[j] = vsubq_u8(hibit_friendly_chars_neon[i].val[j], one);
|
192
|
+
rails_friendly_chars_neon[i].val[j] = vsubq_u8(rails_friendly_chars_neon[i].val[j], one);
|
193
|
+
}
|
246
194
|
}
|
247
|
-
|
248
|
-
|
195
|
+
|
196
|
+
for (int i = 0; i < 4; i++) {
|
197
|
+
for (int j = 0; j < 4; j++) {
|
198
|
+
rails_xss_friendly_chars_neon[i].val[j] = vsubq_u8(rails_xss_friendly_chars_neon[i].val[j], one);
|
199
|
+
}
|
249
200
|
}
|
250
|
-
out->buf = buf;
|
251
|
-
out->end = buf + size;
|
252
|
-
out->cur = out->buf + pos;
|
253
201
|
}
|
202
|
+
#endif
|
254
203
|
|
255
|
-
inline static
|
256
|
-
|
257
|
-
|
204
|
+
inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
|
205
|
+
#ifdef HAVE_SIMD_NEON
|
206
|
+
size_t size = 0;
|
207
|
+
size_t i = 0;
|
258
208
|
|
259
|
-
|
260
|
-
|
261
|
-
*out->cur++ = hex_chars[d];
|
262
|
-
}
|
209
|
+
for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
|
210
|
+
size += sizeof(uint8x16_t);
|
263
211
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
212
|
+
// See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
|
213
|
+
uint8x16_t chunk = vld1q_u8(str);
|
214
|
+
uint8x16_t tmp1 = vqtbl4q_u8(hibit_friendly_chars_neon[0], chunk);
|
215
|
+
uint8x16_t tmp2 = vqtbl4q_u8(hibit_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
|
216
|
+
uint8x16_t result = vorrq_u8(tmp1, tmp2);
|
217
|
+
uint8_t tmp = vaddvq_u8(result);
|
218
|
+
size += tmp;
|
268
219
|
}
|
269
|
-
memcpy(out->cur, str, cnt);
|
270
|
-
out->cur += cnt;
|
271
|
-
*out->cur = '\0';
|
272
|
-
}
|
273
220
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
if (0xC0 == (0xE0 & b)) {
|
281
|
-
cnt = 1;
|
282
|
-
code = b & 0x0000001F;
|
283
|
-
} else if (0xE0 == (0xF0 & b)) {
|
284
|
-
cnt = 2;
|
285
|
-
code = b & 0x0000000F;
|
286
|
-
} else if (0xF0 == (0xF8 & b)) {
|
287
|
-
cnt = 3;
|
288
|
-
code = b & 0x00000007;
|
289
|
-
} else if (0xF8 == (0xFC & b)) {
|
290
|
-
cnt = 4;
|
291
|
-
code = b & 0x00000003;
|
292
|
-
} else if (0xFC == (0xFE & b)) {
|
293
|
-
cnt = 5;
|
294
|
-
code = b & 0x00000001;
|
295
|
-
} else {
|
296
|
-
cnt = 0;
|
297
|
-
rb_raise(rb_eEncodingError, "Invalid Unicode\n");
|
298
|
-
}
|
299
|
-
str++;
|
300
|
-
for (; 0 < cnt; cnt--, str++) {
|
301
|
-
b = *(uint8_t*)str;
|
302
|
-
if (end <= str || 0x80 != (0xC0 & b)) {
|
303
|
-
rb_raise(rb_eEncodingError, "Invalid Unicode\n");
|
304
|
-
}
|
305
|
-
code = (code << 6) | (b & 0x0000003F);
|
306
|
-
}
|
307
|
-
if (0x0000FFFF < code) {
|
308
|
-
uint32_t c1;
|
309
|
-
|
310
|
-
code -= 0x00010000;
|
311
|
-
c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
|
312
|
-
code = (code & 0x000003FF) + 0x0000DC00;
|
313
|
-
*out->cur++ = '\\';
|
314
|
-
*out->cur++ = 'u';
|
315
|
-
for (i = 3; 0 <= i; i--) {
|
316
|
-
*out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
|
317
|
-
}
|
318
|
-
}
|
319
|
-
*out->cur++ = '\\';
|
320
|
-
*out->cur++ = 'u';
|
321
|
-
for (i = 3; 0 <= i; i--) {
|
322
|
-
*out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
|
323
|
-
}
|
324
|
-
return str - 1;
|
221
|
+
size_t total = size + calculate_string_size(str, len - i, hibit_friendly_chars);
|
222
|
+
return total;
|
223
|
+
#else
|
224
|
+
return calculate_string_size(str, len, hibit_friendly_chars);
|
225
|
+
#endif
|
325
226
|
}
|
326
227
|
|
327
|
-
|
328
|
-
|
329
|
-
static long
|
330
|
-
check_circular(VALUE obj, Out out) {
|
331
|
-
slot_t id = 0;
|
332
|
-
slot_t *slot;
|
333
|
-
|
334
|
-
if (ObjectMode == out->opts->mode && Yes == out->opts->circular) {
|
335
|
-
if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
|
336
|
-
out->circ_cnt++;
|
337
|
-
id = out->circ_cnt;
|
338
|
-
*slot = id;
|
339
|
-
} else {
|
340
|
-
if (out->end - out->cur <= 18) {
|
341
|
-
grow(out, 18);
|
342
|
-
}
|
343
|
-
*out->cur++ = '"';
|
344
|
-
*out->cur++ = '^';
|
345
|
-
*out->cur++ = 'r';
|
346
|
-
dump_ulong(id, out);
|
347
|
-
*out->cur++ = '"';
|
348
|
-
|
349
|
-
return -1;
|
350
|
-
}
|
351
|
-
}
|
352
|
-
return (long)id;
|
228
|
+
inline static size_t slash_friendly_size(const uint8_t *str, size_t len) {
|
229
|
+
return calculate_string_size(str, len, slash_friendly_chars);
|
353
230
|
}
|
354
231
|
|
355
|
-
static
|
356
|
-
|
357
|
-
|
232
|
+
inline static size_t ascii_friendly_size(const uint8_t *str, size_t len) {
|
233
|
+
return calculate_string_size(str, len, ascii_friendly_chars);
|
234
|
+
}
|
358
235
|
|
359
|
-
|
360
|
-
|
361
|
-
}
|
362
|
-
*out->cur++ = 'n';
|
363
|
-
*out->cur++ = 'u';
|
364
|
-
*out->cur++ = 'l';
|
365
|
-
*out->cur++ = 'l';
|
366
|
-
*out->cur = '\0';
|
236
|
+
inline static size_t xss_friendly_size(const uint8_t *str, size_t len) {
|
237
|
+
return calculate_string_size(str, len, xss_friendly_chars);
|
367
238
|
}
|
368
239
|
|
369
|
-
static
|
370
|
-
|
371
|
-
size_t
|
240
|
+
inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
|
241
|
+
size_t size = 0;
|
242
|
+
size_t i = len;
|
243
|
+
bool check = false;
|
372
244
|
|
373
|
-
|
374
|
-
|
245
|
+
for (; 0 < i; str++, i--) {
|
246
|
+
size += hixss_friendly_chars[*str];
|
247
|
+
if (0 != (0x80 & *str)) {
|
248
|
+
check = true;
|
249
|
+
}
|
375
250
|
}
|
376
|
-
*
|
377
|
-
*out->cur++ = 'r';
|
378
|
-
*out->cur++ = 'u';
|
379
|
-
*out->cur++ = 'e';
|
380
|
-
*out->cur = '\0';
|
251
|
+
return size - len * (size_t)'0' + check;
|
381
252
|
}
|
382
253
|
|
383
|
-
static
|
384
|
-
|
385
|
-
|
254
|
+
inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
|
255
|
+
long size = 0;
|
256
|
+
uint8_t hi = 0;
|
386
257
|
|
387
|
-
|
388
|
-
|
389
|
-
}
|
390
|
-
*out->cur++ = 'f';
|
391
|
-
*out->cur++ = 'a';
|
392
|
-
*out->cur++ = 'l';
|
393
|
-
*out->cur++ = 's';
|
394
|
-
*out->cur++ = 'e';
|
395
|
-
*out->cur = '\0';
|
396
|
-
}
|
258
|
+
#ifdef HAVE_SIMD_NEON
|
259
|
+
size_t i = 0;
|
397
260
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
long long num = rb_num2ll(obj);
|
403
|
-
int neg = 0;
|
261
|
+
uint8x16_t has_some_hibit = vdupq_n_u8(0);
|
262
|
+
uint8x16_t hibit = vdupq_n_u8(0x80);
|
263
|
+
for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
|
264
|
+
size += sizeof(uint8x16_t);
|
404
265
|
|
405
|
-
|
406
|
-
neg = 1;
|
407
|
-
num = -num;
|
408
|
-
}
|
409
|
-
*b-- = '\0';
|
410
|
-
if (0 < num) {
|
411
|
-
for (; 0 < num; num /= 10, b--) {
|
412
|
-
*b = (num % 10) + '0';
|
413
|
-
}
|
414
|
-
if (neg) {
|
415
|
-
*b = '-';
|
416
|
-
} else {
|
417
|
-
b++;
|
418
|
-
}
|
419
|
-
} else {
|
420
|
-
*b = '0';
|
421
|
-
}
|
422
|
-
if (out->end - out->cur <= (long)(sizeof(buf) - (b - buf))) {
|
423
|
-
grow(out, sizeof(buf) - (b - buf));
|
424
|
-
}
|
425
|
-
for (; '\0' != *b; b++) {
|
426
|
-
*out->cur++ = *b;
|
427
|
-
}
|
428
|
-
*out->cur = '\0';
|
429
|
-
}
|
266
|
+
uint8x16_t chunk = vld1q_u8(str);
|
430
267
|
|
431
|
-
|
432
|
-
|
433
|
-
volatile VALUE rs = rb_big2str(obj, 10);
|
434
|
-
int cnt = (int)RSTRING_LEN(rs);
|
268
|
+
// Check to see if any of these bytes have the high bit set.
|
269
|
+
has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
|
435
270
|
|
436
|
-
|
437
|
-
|
271
|
+
uint8x16_t tmp1 = vqtbl4q_u8(rails_xss_friendly_chars_neon[0], chunk);
|
272
|
+
uint8x16_t tmp2 = vqtbl4q_u8(rails_xss_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
|
273
|
+
uint8x16_t tmp3 = vqtbl4q_u8(rails_xss_friendly_chars_neon[2], veorq_u8(chunk, vdupq_n_u8(0x80)));
|
274
|
+
uint8x16_t tmp4 = vqtbl4q_u8(rails_xss_friendly_chars_neon[3], veorq_u8(chunk, vdupq_n_u8(0xc0)));
|
275
|
+
uint8x16_t result = vorrq_u8(tmp4, vorrq_u8(tmp3, vorrq_u8(tmp1, tmp2)));
|
276
|
+
uint8_t tmp = vaddvq_u8(result);
|
277
|
+
size += tmp;
|
438
278
|
}
|
439
|
-
memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
|
440
|
-
out->cur += cnt;
|
441
|
-
*out->cur = '\0';
|
442
|
-
}
|
443
279
|
|
444
|
-
|
445
|
-
|
446
|
-
static const char nan_val[] = NAN_VAL;
|
280
|
+
// 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
|
281
|
+
hi = vmaxvq_u8(has_some_hibit) != 0;
|
447
282
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
int cnt = 0;
|
455
|
-
|
456
|
-
if (0.0 == d) {
|
457
|
-
b = buf;
|
458
|
-
*b++ = '0';
|
459
|
-
*b++ = '.';
|
460
|
-
*b++ = '0';
|
461
|
-
*b++ = '\0';
|
462
|
-
cnt = 3;
|
463
|
-
} else if (OJ_INFINITY == d) {
|
464
|
-
if (ObjectMode == out->opts->mode) {
|
465
|
-
strcpy(buf, inf_val);
|
466
|
-
cnt = sizeof(inf_val) - 1;
|
467
|
-
} else {
|
468
|
-
NanDump nd = out->opts->dump_opts.nan_dump;
|
469
|
-
|
470
|
-
if (AutoNan == nd) {
|
471
|
-
switch (out->opts->mode) {
|
472
|
-
case CompatMode: nd = WordNan; break;
|
473
|
-
case StrictMode: nd = RaiseNan; break;
|
474
|
-
case NullMode: nd = NullNan; break;
|
475
|
-
default: break;
|
476
|
-
}
|
477
|
-
}
|
478
|
-
switch (nd) {
|
479
|
-
case RaiseNan:
|
480
|
-
raise_strict(obj);
|
481
|
-
break;
|
482
|
-
case WordNan:
|
483
|
-
strcpy(buf, "Infinity");
|
484
|
-
cnt = 8;
|
485
|
-
break;
|
486
|
-
case NullNan:
|
487
|
-
strcpy(buf, "null");
|
488
|
-
cnt = 4;
|
489
|
-
break;
|
490
|
-
case HugeNan:
|
491
|
-
default:
|
492
|
-
strcpy(buf, inf_val);
|
493
|
-
cnt = sizeof(inf_val) - 1;
|
494
|
-
break;
|
495
|
-
}
|
496
|
-
}
|
497
|
-
} else if (-OJ_INFINITY == d) {
|
498
|
-
if (ObjectMode == out->opts->mode) {
|
499
|
-
strcpy(buf, ninf_val);
|
500
|
-
cnt = sizeof(ninf_val) - 1;
|
501
|
-
} else {
|
502
|
-
NanDump nd = out->opts->dump_opts.nan_dump;
|
503
|
-
|
504
|
-
if (AutoNan == nd) {
|
505
|
-
switch (out->opts->mode) {
|
506
|
-
case CompatMode: nd = WordNan; break;
|
507
|
-
case StrictMode: nd = RaiseNan; break;
|
508
|
-
case NullMode: nd = NullNan; break;
|
509
|
-
default: break;
|
510
|
-
}
|
511
|
-
}
|
512
|
-
switch (nd) {
|
513
|
-
case RaiseNan:
|
514
|
-
raise_strict(obj);
|
515
|
-
break;
|
516
|
-
case WordNan:
|
517
|
-
strcpy(buf, "-Infinity");
|
518
|
-
cnt = 9;
|
519
|
-
break;
|
520
|
-
case NullNan:
|
521
|
-
strcpy(buf, "null");
|
522
|
-
cnt = 4;
|
523
|
-
break;
|
524
|
-
case HugeNan:
|
525
|
-
default:
|
526
|
-
strcpy(buf, ninf_val);
|
527
|
-
cnt = sizeof(ninf_val) - 1;
|
528
|
-
break;
|
529
|
-
}
|
530
|
-
}
|
531
|
-
} else if (isnan(d)) {
|
532
|
-
if (ObjectMode == out->opts->mode) {
|
533
|
-
strcpy(buf, nan_val);
|
534
|
-
cnt = sizeof(nan_val) - 1;
|
535
|
-
} else {
|
536
|
-
NanDump nd = out->opts->dump_opts.nan_dump;
|
537
|
-
|
538
|
-
if (AutoNan == nd) {
|
539
|
-
switch (out->opts->mode) {
|
540
|
-
case CompatMode: nd = WordNan; break;
|
541
|
-
case StrictMode: nd = RaiseNan; break;
|
542
|
-
case NullMode: nd = NullNan; break;
|
543
|
-
default: break;
|
544
|
-
}
|
545
|
-
}
|
546
|
-
switch (nd) {
|
547
|
-
case RaiseNan:
|
548
|
-
raise_strict(obj);
|
549
|
-
break;
|
550
|
-
case WordNan:
|
551
|
-
strcpy(buf, "NaN");
|
552
|
-
cnt = 3;
|
553
|
-
break;
|
554
|
-
case NullNan:
|
555
|
-
strcpy(buf, "null");
|
556
|
-
cnt = 4;
|
557
|
-
break;
|
558
|
-
case HugeNan:
|
559
|
-
default:
|
560
|
-
strcpy(buf, nan_val);
|
561
|
-
cnt = sizeof(nan_val) - 1;
|
562
|
-
break;
|
563
|
-
}
|
564
|
-
}
|
565
|
-
} else if (d == (double)(long long int)d) {
|
566
|
-
cnt = snprintf(buf, sizeof(buf), "%.1f", d);
|
567
|
-
} else if (0 == out->opts->float_prec) {
|
568
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
569
|
-
|
570
|
-
cnt = (int)RSTRING_LEN(rstr);
|
571
|
-
if ((int)sizeof(buf) <= cnt) {
|
572
|
-
cnt = sizeof(buf) - 1;
|
573
|
-
}
|
574
|
-
strncpy(buf, rb_string_value_ptr((VALUE*)&rstr), cnt);
|
575
|
-
buf[cnt] = '\0';
|
576
|
-
} else {
|
577
|
-
cnt = snprintf(buf, sizeof(buf), out->opts->float_fmt, d);
|
283
|
+
for (; i < len; str++, i++) {
|
284
|
+
size += rails_xss_friendly_chars[*str] - '0';
|
285
|
+
hi |= *str & 0x80;
|
286
|
+
}
|
287
|
+
if (0 == hi) {
|
288
|
+
return size;
|
578
289
|
}
|
579
|
-
|
580
|
-
|
290
|
+
return -(size);
|
291
|
+
#else
|
292
|
+
size_t i = len;
|
293
|
+
for (; 0 < i; str++, i--) {
|
294
|
+
size += rails_xss_friendly_chars[*str];
|
295
|
+
hi |= *str & 0x80;
|
581
296
|
}
|
582
|
-
|
583
|
-
|
297
|
+
if (0 == hi) {
|
298
|
+
return size - len * (size_t)'0';
|
584
299
|
}
|
585
|
-
*
|
300
|
+
return -(size - len * (size_t)'0');
|
301
|
+
#endif /* HAVE_SIMD_NEON */
|
586
302
|
}
|
587
303
|
|
588
|
-
static
|
589
|
-
|
590
|
-
|
591
|
-
|
304
|
+
inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
|
305
|
+
long size = 0;
|
306
|
+
uint8_t hi = 0;
|
307
|
+
#ifdef HAVE_SIMD_NEON
|
308
|
+
size_t i = 0;
|
592
309
|
|
593
|
-
|
594
|
-
|
595
|
-
cmap = newline_friendly_chars;
|
596
|
-
size = newline_friendly_size((uint8_t*)str, cnt);
|
597
|
-
break;
|
598
|
-
case ASCIIEsc:
|
599
|
-
cmap = ascii_friendly_chars;
|
600
|
-
size = ascii_friendly_size((uint8_t*)str, cnt);
|
601
|
-
break;
|
602
|
-
case XSSEsc:
|
603
|
-
cmap = xss_friendly_chars;
|
604
|
-
size = xss_friendly_size((uint8_t*)str, cnt);
|
605
|
-
break;
|
606
|
-
case JSONEsc:
|
607
|
-
default:
|
608
|
-
cmap = hibit_friendly_chars;
|
609
|
-
size = hibit_friendly_size((uint8_t*)str, cnt);
|
610
|
-
}
|
611
|
-
if (out->end - out->cur <= (long)size + BUFFER_EXTRA) { // extra 10 for escaped first char, quotes, and sym
|
612
|
-
grow(out, size + BUFFER_EXTRA);
|
613
|
-
}
|
614
|
-
*out->cur++ = '"';
|
615
|
-
if (escape1) {
|
616
|
-
*out->cur++ = '\\';
|
617
|
-
*out->cur++ = 'u';
|
618
|
-
*out->cur++ = '0';
|
619
|
-
*out->cur++ = '0';
|
620
|
-
dump_hex((uint8_t)*str, out);
|
621
|
-
cnt--;
|
622
|
-
size--;
|
623
|
-
str++;
|
624
|
-
is_sym = 0; // just to make sure
|
625
|
-
}
|
626
|
-
if (cnt == size) {
|
627
|
-
if (is_sym) {
|
628
|
-
*out->cur++ = ':';
|
629
|
-
}
|
630
|
-
for (; '\0' != *str; str++) {
|
631
|
-
*out->cur++ = *str;
|
632
|
-
}
|
633
|
-
*out->cur++ = '"';
|
634
|
-
} else {
|
635
|
-
const char *end = str + cnt;
|
636
|
-
|
637
|
-
if (is_sym) {
|
638
|
-
*out->cur++ = ':';
|
639
|
-
}
|
640
|
-
for (; str < end; str++) {
|
641
|
-
switch (cmap[(uint8_t)*str]) {
|
642
|
-
case '1':
|
643
|
-
*out->cur++ = *str;
|
644
|
-
break;
|
645
|
-
case '2':
|
646
|
-
*out->cur++ = '\\';
|
647
|
-
switch (*str) {
|
648
|
-
case '\\': *out->cur++ = '\\'; break;
|
649
|
-
case '\b': *out->cur++ = 'b'; break;
|
650
|
-
case '\t': *out->cur++ = 't'; break;
|
651
|
-
case '\n': *out->cur++ = 'n'; break;
|
652
|
-
case '\f': *out->cur++ = 'f'; break;
|
653
|
-
case '\r': *out->cur++ = 'r'; break;
|
654
|
-
default: *out->cur++ = *str; break;
|
655
|
-
}
|
656
|
-
break;
|
657
|
-
case '3': // Unicode
|
658
|
-
str = dump_unicode(str, end, out);
|
659
|
-
break;
|
660
|
-
case '6': // control characters
|
661
|
-
*out->cur++ = '\\';
|
662
|
-
*out->cur++ = 'u';
|
663
|
-
*out->cur++ = '0';
|
664
|
-
*out->cur++ = '0';
|
665
|
-
dump_hex((uint8_t)*str, out);
|
666
|
-
break;
|
667
|
-
default:
|
668
|
-
break; // ignore, should never happen if the table is correct
|
669
|
-
}
|
670
|
-
}
|
671
|
-
*out->cur++ = '"';
|
672
|
-
}
|
673
|
-
*out->cur = '\0';
|
674
|
-
}
|
310
|
+
uint8x16_t has_some_hibit = vdupq_n_u8(0);
|
311
|
+
uint8x16_t hibit = vdupq_n_u8(0x80);
|
675
312
|
|
676
|
-
|
677
|
-
|
678
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&obj), (int)RSTRING_LEN(obj), 0, 0, out);
|
679
|
-
}
|
313
|
+
for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
|
314
|
+
size += sizeof(uint8x16_t);
|
680
315
|
|
681
|
-
|
682
|
-
|
683
|
-
if (Qundef != clas && rb_cString != clas) {
|
684
|
-
dump_obj_attrs(obj, clas, 0, depth, out);
|
685
|
-
} else {
|
686
|
-
const char *s = rb_string_value_ptr((VALUE*)&obj);
|
687
|
-
size_t len = (size_t)RSTRING_LEN(obj);
|
688
|
-
char s1 = s[1];
|
316
|
+
// See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
|
317
|
+
uint8x16_t chunk = vld1q_u8(str);
|
689
318
|
|
690
|
-
|
319
|
+
// Check to see if any of these bytes have the high bit set.
|
320
|
+
has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
|
321
|
+
|
322
|
+
uint8x16_t tmp1 = vqtbl4q_u8(rails_friendly_chars_neon[0], chunk);
|
323
|
+
uint8x16_t tmp2 = vqtbl4q_u8(rails_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
|
324
|
+
uint8x16_t result = vorrq_u8(tmp1, tmp2);
|
325
|
+
uint8_t tmp = vaddvq_u8(result);
|
326
|
+
size += tmp;
|
691
327
|
}
|
692
|
-
}
|
693
328
|
|
694
|
-
|
695
|
-
|
696
|
-
const char *sym = rb_id2name(SYM2ID(obj));
|
697
|
-
|
698
|
-
dump_cstr(sym, strlen(sym), 0, 0, out);
|
699
|
-
}
|
329
|
+
// 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
|
330
|
+
hi = vmaxvq_u8(has_some_hibit) != 0;
|
700
331
|
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
332
|
+
for (; i < len; str++, i++) {
|
333
|
+
size += rails_friendly_chars[*str] - '0';
|
334
|
+
hi |= *str & 0x80;
|
335
|
+
}
|
336
|
+
if (0 == hi) {
|
337
|
+
return size;
|
338
|
+
}
|
339
|
+
return -(size);
|
340
|
+
#else
|
341
|
+
size_t i = len;
|
342
|
+
for (; 0 < i; str++, i--) {
|
343
|
+
size += rails_friendly_chars[*str];
|
344
|
+
hi |= *str & 0x80;
|
345
|
+
}
|
346
|
+
if (0 == hi) {
|
347
|
+
return size - len * (size_t)'0';
|
348
|
+
}
|
349
|
+
return -(size - len * (size_t)'0');
|
350
|
+
#endif /* HAVE_SIMD_NEON */
|
351
|
+
}
|
352
|
+
|
353
|
+
const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, size_t *lenp) {
|
354
|
+
const char *str = NULL;
|
355
|
+
|
356
|
+
if (AutoNan == opt) {
|
357
|
+
switch (mode) {
|
358
|
+
case CompatMode: opt = WordNan; break;
|
359
|
+
case StrictMode: opt = RaiseNan; break;
|
360
|
+
default: break;
|
361
|
+
}
|
362
|
+
}
|
363
|
+
switch (opt) {
|
364
|
+
case RaiseNan: raise_strict(obj); break;
|
365
|
+
case WordNan:
|
366
|
+
if (plus) {
|
367
|
+
str = "Infinity";
|
368
|
+
*lenp = 8;
|
369
|
+
} else {
|
370
|
+
str = "-Infinity";
|
371
|
+
*lenp = 9;
|
372
|
+
}
|
373
|
+
break;
|
374
|
+
case NullNan:
|
375
|
+
str = "null";
|
376
|
+
*lenp = 4;
|
377
|
+
break;
|
378
|
+
case HugeNan:
|
379
|
+
default:
|
380
|
+
if (plus) {
|
381
|
+
str = inf_val;
|
382
|
+
*lenp = sizeof(inf_val) - 1;
|
383
|
+
} else {
|
384
|
+
str = ninf_val;
|
385
|
+
*lenp = sizeof(ninf_val) - 1;
|
386
|
+
}
|
387
|
+
break;
|
388
|
+
}
|
389
|
+
return str;
|
707
390
|
}
|
708
391
|
|
709
|
-
static void
|
710
|
-
|
711
|
-
const char *s = rb_class2name(obj);
|
392
|
+
inline static void dump_hex(uint8_t c, Out out) {
|
393
|
+
uint8_t d = (c >> 4) & 0x0F;
|
712
394
|
|
713
|
-
|
395
|
+
*out->cur++ = hex_chars[d];
|
396
|
+
d = c & 0x0F;
|
397
|
+
*out->cur++ = hex_chars[d];
|
714
398
|
}
|
715
399
|
|
716
|
-
static void
|
717
|
-
|
718
|
-
|
719
|
-
|
400
|
+
static void raise_invalid_unicode(const char *str, int len, int pos) {
|
401
|
+
char c;
|
402
|
+
char code[32];
|
403
|
+
char *cp = code;
|
404
|
+
int i;
|
405
|
+
uint8_t d;
|
720
406
|
|
721
|
-
|
722
|
-
|
407
|
+
*cp++ = '[';
|
408
|
+
for (i = pos; i < len && i - pos < 5; i++) {
|
409
|
+
c = str[i];
|
410
|
+
d = (c >> 4) & 0x0F;
|
411
|
+
*cp++ = hex_chars[d];
|
412
|
+
d = c & 0x0F;
|
413
|
+
*cp++ = hex_chars[d];
|
414
|
+
*cp++ = ' ';
|
723
415
|
}
|
724
|
-
|
725
|
-
*
|
726
|
-
*
|
727
|
-
|
728
|
-
*out->cur++ = '"';
|
729
|
-
*out->cur++ = ':';
|
730
|
-
dump_cstr(s, len, 0, 0, out);
|
731
|
-
*out->cur++ = '}';
|
732
|
-
*out->cur = '\0';
|
416
|
+
cp--;
|
417
|
+
*cp++ = ']';
|
418
|
+
*cp = '\0';
|
419
|
+
rb_raise(oj_json_generator_error_class, "Invalid Unicode %s at %d", code, pos);
|
733
420
|
}
|
734
421
|
|
735
|
-
static
|
736
|
-
|
737
|
-
|
738
|
-
int
|
739
|
-
int d2 = depth + 1;
|
740
|
-
long id = check_circular(a, out);
|
741
|
-
|
742
|
-
if (id < 0) {
|
743
|
-
return;
|
744
|
-
}
|
745
|
-
if (Qundef != clas && rb_cArray != clas && ObjectMode == out->opts->mode) {
|
746
|
-
dump_obj_attrs(a, clas, 0, depth, out);
|
747
|
-
return;
|
748
|
-
}
|
749
|
-
cnt = (int)RARRAY_LEN(a);
|
750
|
-
*out->cur++ = '[';
|
751
|
-
if (0 < id) {
|
752
|
-
size = d2 * out->indent + 16;
|
753
|
-
if (out->end - out->cur <= (long)size) {
|
754
|
-
grow(out, size);
|
755
|
-
}
|
756
|
-
fill_indent(out, d2);
|
757
|
-
*out->cur++ = '"';
|
758
|
-
*out->cur++ = '^';
|
759
|
-
*out->cur++ = 'i';
|
760
|
-
dump_ulong(id, out);
|
761
|
-
*out->cur++ = '"';
|
762
|
-
}
|
763
|
-
size = 2;
|
764
|
-
if (out->end - out->cur <= (long)size) {
|
765
|
-
grow(out, size);
|
766
|
-
}
|
767
|
-
if (0 == cnt) {
|
768
|
-
*out->cur++ = ']';
|
769
|
-
} else {
|
770
|
-
if (0 < id) {
|
771
|
-
*out->cur++ = ',';
|
772
|
-
}
|
773
|
-
if (out->opts->dump_opts.use) {
|
774
|
-
size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
|
775
|
-
} else {
|
776
|
-
size = d2 * out->indent + 2;
|
777
|
-
}
|
778
|
-
cnt--;
|
779
|
-
for (i = 0; i <= cnt; i++) {
|
780
|
-
if (out->end - out->cur <= (long)size) {
|
781
|
-
grow(out, size);
|
782
|
-
}
|
783
|
-
if (out->opts->dump_opts.use) {
|
784
|
-
if (0 < out->opts->dump_opts.array_size) {
|
785
|
-
strcpy(out->cur, out->opts->dump_opts.array_nl);
|
786
|
-
out->cur += out->opts->dump_opts.array_size;
|
787
|
-
}
|
788
|
-
if (0 < out->opts->dump_opts.indent_size) {
|
789
|
-
int i;
|
790
|
-
for (i = d2; 0 < i; i--) {
|
791
|
-
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
792
|
-
out->cur += out->opts->dump_opts.indent_size;
|
793
|
-
}
|
794
|
-
}
|
795
|
-
} else {
|
796
|
-
fill_indent(out, d2);
|
797
|
-
}
|
798
|
-
dump_val(rb_ary_entry(a, i), d2, out, 0, 0, true);
|
799
|
-
if (i < cnt) {
|
800
|
-
*out->cur++ = ',';
|
801
|
-
}
|
802
|
-
}
|
803
|
-
size = depth * out->indent + 1;
|
804
|
-
if (out->end - out->cur <= (long)size) {
|
805
|
-
grow(out, size);
|
806
|
-
}
|
807
|
-
if (out->opts->dump_opts.use) {
|
808
|
-
//printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
|
809
|
-
if (0 < out->opts->dump_opts.array_size) {
|
810
|
-
strcpy(out->cur, out->opts->dump_opts.array_nl);
|
811
|
-
out->cur += out->opts->dump_opts.array_size;
|
812
|
-
}
|
813
|
-
if (0 < out->opts->dump_opts.indent_size) {
|
814
|
-
int i;
|
815
|
-
|
816
|
-
for (i = depth; 0 < i; i--) {
|
817
|
-
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
818
|
-
out->cur += out->opts->dump_opts.indent_size;
|
819
|
-
}
|
820
|
-
}
|
821
|
-
} else {
|
822
|
-
fill_indent(out, depth);
|
823
|
-
}
|
824
|
-
*out->cur++ = ']';
|
825
|
-
}
|
826
|
-
*out->cur = '\0';
|
827
|
-
}
|
422
|
+
static const char *dump_unicode(const char *str, const char *end, Out out, const char *orig) {
|
423
|
+
uint32_t code = 0;
|
424
|
+
uint8_t b = *(uint8_t *)str;
|
425
|
+
int i, cnt;
|
828
426
|
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
if (
|
836
|
-
|
837
|
-
|
838
|
-
if (
|
839
|
-
|
840
|
-
|
841
|
-
if (
|
842
|
-
|
843
|
-
|
844
|
-
grow(out, size);
|
845
|
-
}
|
846
|
-
fill_indent(out, depth);
|
847
|
-
if (rtype == T_STRING) {
|
848
|
-
dump_str_comp(key, out);
|
849
|
-
} else {
|
850
|
-
dump_sym_comp(key, out);
|
851
|
-
}
|
852
|
-
*out->cur++ = ':';
|
427
|
+
if (0xC0 == (0xE0 & b)) {
|
428
|
+
cnt = 1;
|
429
|
+
code = b & 0x0000001F;
|
430
|
+
} else if (0xE0 == (0xF0 & b)) {
|
431
|
+
cnt = 2;
|
432
|
+
code = b & 0x0000000F;
|
433
|
+
} else if (0xF0 == (0xF8 & b)) {
|
434
|
+
cnt = 3;
|
435
|
+
code = b & 0x00000007;
|
436
|
+
} else if (0xF8 == (0xFC & b)) {
|
437
|
+
cnt = 4;
|
438
|
+
code = b & 0x00000003;
|
439
|
+
} else if (0xFC == (0xFE & b)) {
|
440
|
+
cnt = 5;
|
441
|
+
code = b & 0x00000001;
|
853
442
|
} else {
|
854
|
-
|
855
|
-
|
856
|
-
grow(out, size);
|
857
|
-
}
|
858
|
-
if (0 < out->opts->dump_opts.hash_size) {
|
859
|
-
strcpy(out->cur, out->opts->dump_opts.hash_nl);
|
860
|
-
out->cur += out->opts->dump_opts.hash_size;
|
861
|
-
}
|
862
|
-
if (0 < out->opts->dump_opts.indent_size) {
|
863
|
-
int i;
|
864
|
-
for (i = depth; 0 < i; i--) {
|
865
|
-
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
866
|
-
out->cur += out->opts->dump_opts.indent_size;
|
867
|
-
}
|
868
|
-
}
|
869
|
-
if (rtype == T_STRING) {
|
870
|
-
dump_str_comp(key, out);
|
871
|
-
} else {
|
872
|
-
dump_sym_comp(key, out);
|
873
|
-
}
|
874
|
-
size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
|
875
|
-
if (out->end - out->cur <= size) {
|
876
|
-
grow(out, size);
|
877
|
-
}
|
878
|
-
if (0 < out->opts->dump_opts.before_size) {
|
879
|
-
strcpy(out->cur, out->opts->dump_opts.before_sep);
|
880
|
-
out->cur += out->opts->dump_opts.before_size;
|
881
|
-
}
|
882
|
-
*out->cur++ = ':';
|
883
|
-
if (0 < out->opts->dump_opts.after_size) {
|
884
|
-
strcpy(out->cur, out->opts->dump_opts.after_sep);
|
885
|
-
out->cur += out->opts->dump_opts.after_size;
|
886
|
-
}
|
887
|
-
}
|
888
|
-
dump_val(value, depth, out, 0, 0, false);
|
889
|
-
out->depth = depth;
|
890
|
-
*out->cur++ = ',';
|
891
|
-
|
892
|
-
return ST_CONTINUE;
|
893
|
-
}
|
894
|
-
|
895
|
-
static int
|
896
|
-
hash_cb_compat(VALUE key, VALUE value, Out out) {
|
897
|
-
int depth = out->depth;
|
898
|
-
long size;
|
899
|
-
|
900
|
-
if (out->omit_nil && Qnil == value) {
|
901
|
-
return ST_CONTINUE;
|
443
|
+
cnt = 0;
|
444
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
902
445
|
}
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
|
911
|
-
if (out->end - out->cur <= size) {
|
912
|
-
grow(out, size);
|
913
|
-
}
|
914
|
-
if (0 < out->opts->dump_opts.hash_size) {
|
915
|
-
strcpy(out->cur, out->opts->dump_opts.hash_nl);
|
916
|
-
out->cur += out->opts->dump_opts.hash_size;
|
917
|
-
}
|
918
|
-
if (0 < out->opts->dump_opts.indent_size) {
|
919
|
-
int i;
|
920
|
-
for (i = depth; 0 < i; i--) {
|
921
|
-
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
922
|
-
out->cur += out->opts->dump_opts.indent_size;
|
923
|
-
}
|
924
|
-
}
|
925
|
-
}
|
926
|
-
switch (rb_type(key)) {
|
927
|
-
case T_STRING:
|
928
|
-
dump_str_comp(key, out);
|
929
|
-
break;
|
930
|
-
case T_SYMBOL:
|
931
|
-
dump_sym_comp(key, out);
|
932
|
-
break;
|
933
|
-
default:
|
934
|
-
/*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
|
935
|
-
dump_str_comp(rb_funcall(key, oj_to_s_id, 0), out);
|
936
|
-
break;
|
446
|
+
str++;
|
447
|
+
for (; 0 < cnt; cnt--, str++) {
|
448
|
+
b = *(uint8_t *)str;
|
449
|
+
if (end <= str || 0x80 != (0xC0 & b)) {
|
450
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
451
|
+
}
|
452
|
+
code = (code << 6) | (b & 0x0000003F);
|
937
453
|
}
|
938
|
-
if (
|
939
|
-
|
940
|
-
} else {
|
941
|
-
size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
|
942
|
-
if (out->end - out->cur <= size) {
|
943
|
-
grow(out, size);
|
944
|
-
}
|
945
|
-
if (0 < out->opts->dump_opts.before_size) {
|
946
|
-
strcpy(out->cur, out->opts->dump_opts.before_sep);
|
947
|
-
out->cur += out->opts->dump_opts.before_size;
|
948
|
-
}
|
949
|
-
*out->cur++ = ':';
|
950
|
-
if (0 < out->opts->dump_opts.after_size) {
|
951
|
-
strcpy(out->cur, out->opts->dump_opts.after_sep);
|
952
|
-
out->cur += out->opts->dump_opts.after_size;
|
953
|
-
}
|
954
|
-
}
|
955
|
-
dump_val(value, depth, out, 0, 0, true);
|
956
|
-
out->depth = depth;
|
957
|
-
*out->cur++ = ',';
|
958
|
-
|
959
|
-
return ST_CONTINUE;
|
960
|
-
}
|
454
|
+
if (0x0000FFFF < code) {
|
455
|
+
uint32_t c1;
|
961
456
|
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
}
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
dump_str_obj(key, Qundef, depth, out);
|
976
|
-
*out->cur++ = ':';
|
977
|
-
dump_val(value, depth, out, 0, 0, true);
|
978
|
-
} else if (rb_type(key) == T_SYMBOL) {
|
979
|
-
dump_sym_obj(key, out);
|
980
|
-
*out->cur++ = ':';
|
981
|
-
dump_val(value, depth, out, 0, 0, true);
|
982
|
-
} else {
|
983
|
-
int d2 = depth + 1;
|
984
|
-
long s2 = size + out->indent + 1;
|
985
|
-
int i;
|
986
|
-
int started = 0;
|
987
|
-
uint8_t b;
|
988
|
-
|
989
|
-
if (out->end - out->cur <= s2 + 15) {
|
990
|
-
grow(out, s2 + 15);
|
991
|
-
}
|
992
|
-
*out->cur++ = '"';
|
993
|
-
*out->cur++ = '^';
|
994
|
-
*out->cur++ = '#';
|
995
|
-
out->hash_cnt++;
|
996
|
-
for (i = 28; 0 <= i; i -= 4) {
|
997
|
-
b = (uint8_t)((out->hash_cnt >> i) & 0x0000000F);
|
998
|
-
if ('\0' != b) {
|
999
|
-
started = 1;
|
1000
|
-
}
|
1001
|
-
if (started) {
|
1002
|
-
*out->cur++ = hex_chars[b];
|
1003
|
-
}
|
1004
|
-
}
|
1005
|
-
*out->cur++ = '"';
|
1006
|
-
*out->cur++ = ':';
|
1007
|
-
*out->cur++ = '[';
|
1008
|
-
fill_indent(out, d2);
|
1009
|
-
dump_val(key, d2, out, 0, 0, true);
|
1010
|
-
if (out->end - out->cur <= (long)s2) {
|
1011
|
-
grow(out, s2);
|
1012
|
-
}
|
1013
|
-
*out->cur++ = ',';
|
1014
|
-
fill_indent(out, d2);
|
1015
|
-
dump_val(value, d2, out, 0, 0, true);
|
1016
|
-
if (out->end - out->cur <= (long)size) {
|
1017
|
-
grow(out, size);
|
1018
|
-
}
|
1019
|
-
fill_indent(out, depth);
|
1020
|
-
*out->cur++ = ']';
|
1021
|
-
}
|
1022
|
-
out->depth = depth;
|
1023
|
-
*out->cur++ = ',';
|
1024
|
-
|
1025
|
-
return ST_CONTINUE;
|
457
|
+
code -= 0x00010000;
|
458
|
+
c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
|
459
|
+
code = (code & 0x000003FF) + 0x0000DC00;
|
460
|
+
APPEND_CHARS(out->cur, "\\u", 2);
|
461
|
+
for (i = 3; 0 <= i; i--) {
|
462
|
+
*out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
|
463
|
+
}
|
464
|
+
}
|
465
|
+
APPEND_CHARS(out->cur, "\\u", 2);
|
466
|
+
for (i = 3; 0 <= i; i--) {
|
467
|
+
*out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
|
468
|
+
}
|
469
|
+
return str - 1;
|
1026
470
|
}
|
1027
471
|
|
1028
|
-
static
|
1029
|
-
|
1030
|
-
int
|
1031
|
-
size_t size;
|
472
|
+
static const char *check_unicode(const char *str, const char *end, const char *orig) {
|
473
|
+
uint8_t b = *(uint8_t *)str;
|
474
|
+
int cnt = 0;
|
1032
475
|
|
1033
|
-
if (
|
1034
|
-
|
1035
|
-
|
476
|
+
if (0xC0 == (0xE0 & b)) {
|
477
|
+
cnt = 1;
|
478
|
+
} else if (0xE0 == (0xF0 & b)) {
|
479
|
+
cnt = 2;
|
480
|
+
} else if (0xF0 == (0xF8 & b)) {
|
481
|
+
cnt = 3;
|
482
|
+
} else if (0xF8 == (0xFC & b)) {
|
483
|
+
cnt = 4;
|
484
|
+
} else if (0xFC == (0xFE & b)) {
|
485
|
+
cnt = 5;
|
486
|
+
} else {
|
487
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
1036
488
|
}
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
489
|
+
str++;
|
490
|
+
for (; 0 < cnt; cnt--, str++) {
|
491
|
+
b = *(uint8_t *)str;
|
492
|
+
if (end <= str || 0x80 != (0xC0 & b)) {
|
493
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
494
|
+
}
|
495
|
+
}
|
496
|
+
return str;
|
497
|
+
}
|
498
|
+
|
499
|
+
// Returns 0 if not using circular references, -1 if no further writing is
|
500
|
+
// needed (duplicate), and a positive value if the object was added to the
|
501
|
+
// cache.
|
502
|
+
long oj_check_circular(VALUE obj, Out out) {
|
503
|
+
slot_t id = 0;
|
504
|
+
slot_t *slot;
|
505
|
+
|
506
|
+
if (Yes == out->opts->circular) {
|
507
|
+
if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
|
508
|
+
out->circ_cnt++;
|
509
|
+
id = out->circ_cnt;
|
510
|
+
*slot = id;
|
511
|
+
} else {
|
512
|
+
if (ObjectMode == out->opts->mode) {
|
513
|
+
assure_size(out, 18);
|
514
|
+
APPEND_CHARS(out->cur, "\"^r", 3);
|
515
|
+
dump_ulong(id, out);
|
516
|
+
*out->cur++ = '"';
|
517
|
+
}
|
518
|
+
return -1;
|
519
|
+
}
|
1041
520
|
}
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
521
|
+
return (long)id;
|
522
|
+
}
|
523
|
+
|
524
|
+
void oj_dump_time(VALUE obj, Out out, int withZone) {
|
525
|
+
char buf[64];
|
526
|
+
char *b = buf + sizeof(buf) - 1;
|
527
|
+
long size;
|
528
|
+
char *dot;
|
529
|
+
int neg = 0;
|
530
|
+
long one = 1000000000;
|
531
|
+
long long sec;
|
532
|
+
long long nsec;
|
533
|
+
|
534
|
+
// rb_time_timespec as well as rb_time_timeeval have a bug that causes an
|
535
|
+
// exception to be raised if a time is before 1970 on 32 bit systems so
|
536
|
+
// check the timespec size and use the ruby calls if a 32 bit system.
|
537
|
+
if (16 <= sizeof(struct timespec)) {
|
538
|
+
struct timespec ts = rb_time_timespec(obj);
|
539
|
+
|
540
|
+
sec = (long long)ts.tv_sec;
|
541
|
+
nsec = ts.tv_nsec;
|
1045
542
|
} else {
|
1046
|
-
|
1047
|
-
|
1048
|
-
if (0 > id) {
|
1049
|
-
return;
|
1050
|
-
}
|
1051
|
-
*out->cur++ = '{';
|
1052
|
-
if (0 < id) {
|
1053
|
-
if (out->end - out->cur <= (long)size + 16) {
|
1054
|
-
grow(out, size + 16);
|
1055
|
-
}
|
1056
|
-
fill_indent(out, depth + 1);
|
1057
|
-
*out->cur++ = '"';
|
1058
|
-
*out->cur++ = '^';
|
1059
|
-
*out->cur++ = 'i';
|
1060
|
-
*out->cur++ = '"';
|
1061
|
-
*out->cur++ = ':';
|
1062
|
-
dump_ulong(id, out);
|
1063
|
-
*out->cur++ = ',';
|
1064
|
-
}
|
1065
|
-
out->depth = depth + 1;
|
1066
|
-
if (ObjectMode == mode) {
|
1067
|
-
rb_hash_foreach(obj, hash_cb_object, (VALUE)out);
|
1068
|
-
} else if (CompatMode == mode) {
|
1069
|
-
rb_hash_foreach(obj, hash_cb_compat, (VALUE)out);
|
1070
|
-
} else {
|
1071
|
-
rb_hash_foreach(obj, hash_cb_strict, (VALUE)out);
|
1072
|
-
}
|
1073
|
-
if (',' == *(out->cur - 1)) {
|
1074
|
-
out->cur--; // backup to overwrite last comma
|
1075
|
-
}
|
1076
|
-
if (!out->opts->dump_opts.use) {
|
1077
|
-
if (out->end - out->cur <= (long)size) {
|
1078
|
-
grow(out, size);
|
1079
|
-
}
|
1080
|
-
fill_indent(out, depth);
|
1081
|
-
} else {
|
1082
|
-
size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
|
1083
|
-
if (out->end - out->cur <= (long)size) {
|
1084
|
-
grow(out, size);
|
1085
|
-
}
|
1086
|
-
if (0 < out->opts->dump_opts.hash_size) {
|
1087
|
-
strcpy(out->cur, out->opts->dump_opts.hash_nl);
|
1088
|
-
out->cur += out->opts->dump_opts.hash_size;
|
1089
|
-
}
|
1090
|
-
if (0 < out->opts->dump_opts.indent_size) {
|
1091
|
-
int i;
|
1092
|
-
|
1093
|
-
for (i = depth; 0 < i; i--) {
|
1094
|
-
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
1095
|
-
out->cur += out->opts->dump_opts.indent_size;
|
1096
|
-
}
|
1097
|
-
}
|
1098
|
-
}
|
1099
|
-
*out->cur++ = '}';
|
543
|
+
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
544
|
+
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
1100
545
|
}
|
1101
|
-
*out->cur = '\0';
|
1102
|
-
}
|
1103
546
|
|
1104
|
-
static void
|
1105
|
-
dump_time(VALUE obj, Out out, int withZone) {
|
1106
|
-
char buf[64];
|
1107
|
-
char *b = buf + sizeof(buf) - 1;
|
1108
|
-
long size;
|
1109
|
-
char *dot;
|
1110
|
-
int neg = 0;
|
1111
|
-
long one = 1000000000;
|
1112
|
-
#if HAS_RB_TIME_TIMESPEC
|
1113
|
-
struct timespec ts = rb_time_timespec(obj);
|
1114
|
-
time_t sec = ts.tv_sec;
|
1115
|
-
long nsec = ts.tv_nsec;
|
1116
|
-
#else
|
1117
|
-
time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
1118
|
-
#if HAS_NANO_TIME
|
1119
|
-
long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
1120
|
-
#else
|
1121
|
-
long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
|
1122
|
-
#endif
|
1123
|
-
#endif
|
1124
|
-
|
1125
547
|
*b-- = '\0';
|
1126
548
|
if (withZone) {
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
549
|
+
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
550
|
+
int zneg = (0 > tzsecs);
|
551
|
+
|
552
|
+
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
553
|
+
tzsecs = 86400;
|
554
|
+
}
|
555
|
+
if (zneg) {
|
556
|
+
tzsecs = -tzsecs;
|
557
|
+
}
|
558
|
+
if (0 == tzsecs) {
|
559
|
+
*b-- = '0';
|
560
|
+
} else {
|
561
|
+
for (; 0 < tzsecs; b--, tzsecs /= 10) {
|
562
|
+
*b = '0' + (tzsecs % 10);
|
563
|
+
}
|
564
|
+
if (zneg) {
|
565
|
+
*b-- = '-';
|
566
|
+
}
|
567
|
+
}
|
568
|
+
*b-- = 'e';
|
1147
569
|
}
|
1148
570
|
if (0 > sec) {
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
571
|
+
neg = 1;
|
572
|
+
sec = -sec;
|
573
|
+
if (0 < nsec) {
|
574
|
+
nsec = 1000000000 - nsec;
|
575
|
+
sec--;
|
576
|
+
}
|
1155
577
|
}
|
1156
578
|
dot = b - 9;
|
1157
579
|
if (0 < out->opts->sec_prec) {
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
580
|
+
if (9 > out->opts->sec_prec) {
|
581
|
+
int i;
|
582
|
+
|
583
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
584
|
+
dot++;
|
585
|
+
nsec = (nsec + 5) / 10;
|
586
|
+
one /= 10;
|
587
|
+
}
|
588
|
+
}
|
589
|
+
if (one <= nsec) {
|
590
|
+
nsec -= one;
|
591
|
+
sec++;
|
592
|
+
}
|
593
|
+
for (; dot < b; b--, nsec /= 10) {
|
594
|
+
*b = '0' + (nsec % 10);
|
595
|
+
}
|
596
|
+
*b-- = '.';
|
1175
597
|
}
|
1176
598
|
if (0 == sec) {
|
1177
|
-
|
599
|
+
*b-- = '0';
|
1178
600
|
} else {
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
601
|
+
for (; 0 < sec; b--, sec /= 10) {
|
602
|
+
*b = '0' + (sec % 10);
|
603
|
+
}
|
1182
604
|
}
|
1183
605
|
if (neg) {
|
1184
|
-
|
606
|
+
*b-- = '-';
|
1185
607
|
}
|
1186
608
|
b++;
|
1187
609
|
size = sizeof(buf) - (b - buf) - 1;
|
1188
|
-
|
1189
|
-
|
1190
|
-
}
|
1191
|
-
memcpy(out->cur, b, size);
|
1192
|
-
out->cur += size;
|
610
|
+
assure_size(out, size);
|
611
|
+
APPEND_CHARS(out->cur, b, size);
|
1193
612
|
*out->cur = '\0';
|
1194
613
|
}
|
1195
614
|
|
1196
|
-
|
1197
|
-
|
1198
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
615
|
+
void oj_dump_ruby_time(VALUE obj, Out out) {
|
616
|
+
volatile VALUE rstr = oj_safe_string_convert(obj);
|
1199
617
|
|
1200
|
-
|
618
|
+
oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
|
1201
619
|
}
|
1202
620
|
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
#else
|
1213
|
-
time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
1214
|
-
#if HAS_NANO_TIME
|
1215
|
-
long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
1216
|
-
#else
|
1217
|
-
long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
|
1218
|
-
#endif
|
1219
|
-
#endif
|
1220
|
-
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
1221
|
-
int tzhour, tzmin;
|
1222
|
-
char tzsign = '+';
|
621
|
+
void oj_dump_xml_time(VALUE obj, Out out) {
|
622
|
+
char buf[64];
|
623
|
+
struct _timeInfo ti;
|
624
|
+
long one = 1000000000;
|
625
|
+
int64_t sec;
|
626
|
+
long long nsec;
|
627
|
+
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
628
|
+
int tzhour, tzmin;
|
629
|
+
char tzsign = '+';
|
1223
630
|
|
1224
|
-
if (
|
1225
|
-
|
631
|
+
if (16 <= sizeof(struct timespec)) {
|
632
|
+
struct timespec ts = rb_time_timespec(obj);
|
633
|
+
|
634
|
+
sec = ts.tv_sec;
|
635
|
+
nsec = ts.tv_nsec;
|
636
|
+
} else {
|
637
|
+
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
638
|
+
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
1226
639
|
}
|
1227
|
-
if (9 > out->opts->sec_prec) {
|
1228
|
-
int i;
|
1229
640
|
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
641
|
+
assure_size(out, 36);
|
642
|
+
if (9 > out->opts->sec_prec) {
|
643
|
+
int i;
|
644
|
+
|
645
|
+
// This is pretty lame but to be compatible with rails and active
|
646
|
+
// support rounding is not done but instead a floor is done when
|
647
|
+
// second precision is 3 just to be like rails. sigh.
|
648
|
+
if (3 == out->opts->sec_prec) {
|
649
|
+
nsec /= 1000000;
|
650
|
+
one = 1000;
|
651
|
+
} else {
|
652
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
653
|
+
nsec = (nsec + 5) / 10;
|
654
|
+
one /= 10;
|
655
|
+
}
|
656
|
+
if (one <= nsec) {
|
657
|
+
nsec -= one;
|
658
|
+
sec++;
|
659
|
+
}
|
660
|
+
}
|
1238
661
|
}
|
1239
662
|
// 2012-01-05T23:58:07.123456000+09:00
|
1240
|
-
//tm = localtime(&sec);
|
663
|
+
// tm = localtime(&sec);
|
1241
664
|
sec += tzsecs;
|
1242
|
-
|
1243
|
-
#if 1
|
665
|
+
sec_as_time((int64_t)sec, &ti);
|
1244
666
|
if (0 > tzsecs) {
|
1245
667
|
tzsign = '-';
|
1246
668
|
tzhour = (int)(tzsecs / -3600);
|
1247
|
-
tzmin
|
669
|
+
tzmin = (int)(tzsecs / -60) - (tzhour * 60);
|
1248
670
|
} else {
|
1249
671
|
tzhour = (int)(tzsecs / 3600);
|
1250
|
-
tzmin
|
1251
|
-
}
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
1271
|
-
tm->tm_hour, tm->tm_min, tm->tm_sec,
|
1272
|
-
tzsign, tzhour, tzmin);
|
1273
|
-
dump_cstr(buf, 25, 0, 0, out);
|
1274
|
-
}
|
672
|
+
tzmin = (int)(tzsecs / 60) - (tzhour * 60);
|
673
|
+
}
|
674
|
+
if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
|
675
|
+
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
676
|
+
int len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
|
677
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
678
|
+
} else {
|
679
|
+
int len = sprintf(buf,
|
680
|
+
"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
|
681
|
+
ti.year,
|
682
|
+
ti.mon,
|
683
|
+
ti.day,
|
684
|
+
ti.hour,
|
685
|
+
ti.min,
|
686
|
+
ti.sec,
|
687
|
+
tzsign,
|
688
|
+
tzhour,
|
689
|
+
tzmin);
|
690
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
691
|
+
}
|
1275
692
|
} else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
1285
|
-
tm->tm_hour, tm->tm_min, tm->tm_sec, nsec);
|
1286
|
-
dump_cstr(buf, len, 0, 0, out);
|
1287
|
-
} else {
|
1288
|
-
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
|
1289
|
-
int len = 35;
|
1290
|
-
|
1291
|
-
if (9 > out->opts->sec_prec) {
|
1292
|
-
format[32] = '0' + out->opts->sec_prec;
|
1293
|
-
len -= 9 - out->opts->sec_prec;
|
1294
|
-
}
|
1295
|
-
sprintf(buf, format,
|
1296
|
-
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
1297
|
-
tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
|
1298
|
-
tzsign, tzhour, tzmin);
|
1299
|
-
dump_cstr(buf, len, 0, 0, out);
|
1300
|
-
}
|
1301
|
-
}
|
1302
|
-
|
1303
|
-
static void
|
1304
|
-
dump_data_strict(VALUE obj, Out out) {
|
1305
|
-
VALUE clas = rb_obj_class(obj);
|
1306
|
-
|
1307
|
-
if (oj_bigdecimal_class == clas) {
|
1308
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1309
|
-
|
1310
|
-
dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
|
1311
|
-
} else {
|
1312
|
-
raise_strict(obj);
|
1313
|
-
}
|
1314
|
-
}
|
1315
|
-
|
1316
|
-
static void
|
1317
|
-
dump_data_null(VALUE obj, Out out) {
|
1318
|
-
VALUE clas = rb_obj_class(obj);
|
1319
|
-
|
1320
|
-
if (oj_bigdecimal_class == clas) {
|
1321
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1322
|
-
|
1323
|
-
dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
|
1324
|
-
} else {
|
1325
|
-
dump_nil(out);
|
1326
|
-
}
|
1327
|
-
}
|
1328
|
-
|
1329
|
-
static void
|
1330
|
-
dump_data_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
|
1331
|
-
VALUE clas = rb_obj_class(obj);
|
1332
|
-
|
1333
|
-
if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
|
1334
|
-
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
|
1335
|
-
|
1336
|
-
if (T_HASH != rb_type(h)) {
|
1337
|
-
// It seems that ActiveRecord implemented to_hash so that it returns
|
1338
|
-
// an Array and not a Hash. To get around that any value returned
|
1339
|
-
// will be dumped.
|
1340
|
-
|
1341
|
-
//rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
1342
|
-
dump_val(h, depth, out, 0, 0, false);
|
1343
|
-
}
|
1344
|
-
dump_hash(h, Qundef, depth, out->opts->mode, out);
|
1345
|
-
} else if (Yes == out->opts->bigdec_as_num && oj_bigdecimal_class == clas) {
|
1346
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1347
|
-
|
1348
|
-
dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
|
1349
|
-
} else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
|
1350
|
-
volatile VALUE aj;
|
1351
|
-
|
1352
|
-
#if HAS_METHOD_ARITY
|
1353
|
-
// Some classes elect to not take an options argument so check the arity
|
1354
|
-
// of as_json.
|
1355
|
-
switch (rb_obj_method_arity(obj, oj_as_json_id)) {
|
1356
|
-
case 0:
|
1357
|
-
aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
|
1358
|
-
break;
|
1359
|
-
case 1:
|
1360
|
-
if (1 <= argc) {
|
1361
|
-
aj = rb_funcall2(obj, oj_as_json_id, 1, argv);
|
1362
|
-
} else {
|
1363
|
-
VALUE nothing [1];
|
1364
|
-
|
1365
|
-
nothing[0] = Qnil;
|
1366
|
-
aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
|
1367
|
-
}
|
1368
|
-
break;
|
1369
|
-
default:
|
1370
|
-
aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
|
1371
|
-
break;
|
1372
|
-
}
|
1373
|
-
#else
|
1374
|
-
aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
|
1375
|
-
#endif
|
1376
|
-
// Catch the obvious brain damaged recursive dumping.
|
1377
|
-
if (aj == obj) {
|
1378
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1379
|
-
|
1380
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1381
|
-
} else {
|
1382
|
-
dump_val(aj, depth, out, 0, 0, false);
|
1383
|
-
}
|
1384
|
-
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
1385
|
-
volatile VALUE rs;
|
1386
|
-
const char *s;
|
1387
|
-
int len;
|
1388
|
-
|
1389
|
-
rs = rb_funcall(obj, oj_to_json_id, 0);
|
1390
|
-
s = rb_string_value_ptr((VALUE*)&rs);
|
1391
|
-
len = (int)RSTRING_LEN(rs);
|
1392
|
-
|
1393
|
-
if (out->end - out->cur <= len + 1) {
|
1394
|
-
grow(out, len);
|
1395
|
-
}
|
1396
|
-
memcpy(out->cur, s, len);
|
1397
|
-
out->cur += len;
|
1398
|
-
*out->cur = '\0';
|
1399
|
-
} else {
|
1400
|
-
if (rb_cTime == clas) {
|
1401
|
-
switch (out->opts->time_format) {
|
1402
|
-
case RubyTime: dump_ruby_time(obj, out); break;
|
1403
|
-
case XmlTime: dump_xml_time(obj, out); break;
|
1404
|
-
case UnixZTime: dump_time(obj, out, 1); break;
|
1405
|
-
case UnixTime:
|
1406
|
-
default: dump_time(obj, out, 0); break;
|
1407
|
-
}
|
1408
|
-
} else if (oj_bigdecimal_class == clas) {
|
1409
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1410
|
-
|
1411
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1412
|
-
} else {
|
1413
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1414
|
-
|
1415
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1416
|
-
}
|
1417
|
-
}
|
1418
|
-
}
|
1419
|
-
|
1420
|
-
static void
|
1421
|
-
dump_data_obj(VALUE obj, int depth, Out out) {
|
1422
|
-
VALUE clas = rb_obj_class(obj);
|
1423
|
-
|
1424
|
-
if (rb_cTime == clas) {
|
1425
|
-
if (out->end - out->cur <= 6) {
|
1426
|
-
grow(out, 6);
|
1427
|
-
}
|
1428
|
-
*out->cur++ = '{';
|
1429
|
-
*out->cur++ = '"';
|
1430
|
-
*out->cur++ = '^';
|
1431
|
-
*out->cur++ = 't';
|
1432
|
-
*out->cur++ = '"';
|
1433
|
-
*out->cur++ = ':';
|
1434
|
-
switch (out->opts->time_format) {
|
1435
|
-
case RubyTime: // Does not output fractional seconds
|
1436
|
-
case XmlTime:
|
1437
|
-
dump_xml_time(obj, out);
|
1438
|
-
break;
|
1439
|
-
case UnixZTime:
|
1440
|
-
dump_time(obj, out, 1);
|
1441
|
-
break;
|
1442
|
-
case UnixTime:
|
1443
|
-
default:
|
1444
|
-
dump_time(obj, out, 0);
|
1445
|
-
break;
|
1446
|
-
}
|
1447
|
-
*out->cur++ = '}';
|
1448
|
-
*out->cur = '\0';
|
1449
|
-
} else {
|
1450
|
-
if (oj_bigdecimal_class == clas) {
|
1451
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1452
|
-
|
1453
|
-
if (Yes == out->opts->bigdec_as_num) {
|
1454
|
-
dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
|
1455
|
-
} else {
|
1456
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1457
|
-
}
|
1458
|
-
} else {
|
1459
|
-
dump_nil(out);
|
1460
|
-
}
|
1461
|
-
}
|
1462
|
-
}
|
1463
|
-
|
1464
|
-
static void
|
1465
|
-
dump_obj_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
|
1466
|
-
if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
|
1467
|
-
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
|
1468
|
-
|
1469
|
-
if (T_HASH != rb_type(h)) {
|
1470
|
-
// It seems that ActiveRecord implemented to_hash so that it returns
|
1471
|
-
// an Array and not a Hash. To get around that any value returned
|
1472
|
-
// will be dumped.
|
1473
|
-
|
1474
|
-
//rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
1475
|
-
dump_val(h, depth, out, 0, 0, false);
|
1476
|
-
} else {
|
1477
|
-
dump_hash(h, Qundef, depth, out->opts->mode, out);
|
1478
|
-
}
|
1479
|
-
} else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
|
1480
|
-
volatile VALUE aj;
|
1481
|
-
|
1482
|
-
#if HAS_METHOD_ARITY
|
1483
|
-
// Some classes elect to not take an options argument so check the arity
|
1484
|
-
// of as_json.
|
1485
|
-
switch (rb_obj_method_arity(obj, oj_as_json_id)) {
|
1486
|
-
case 0:
|
1487
|
-
aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
|
1488
|
-
break;
|
1489
|
-
case 1:
|
1490
|
-
if (1 <= argc) {
|
1491
|
-
aj = rb_funcall2(obj, oj_as_json_id, 1, argv);
|
1492
|
-
} else {
|
1493
|
-
VALUE nothing [1];
|
1494
|
-
|
1495
|
-
nothing[0] = Qnil;
|
1496
|
-
aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
|
1497
|
-
}
|
1498
|
-
break;
|
1499
|
-
default:
|
1500
|
-
aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
|
1501
|
-
break;
|
1502
|
-
}
|
1503
|
-
#else
|
1504
|
-
aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
|
1505
|
-
#endif
|
1506
|
-
// Catch the obvious brain damaged recursive dumping.
|
1507
|
-
if (aj == obj) {
|
1508
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1509
|
-
|
1510
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1511
|
-
} else {
|
1512
|
-
dump_val(aj, depth, out, 0, 0, false);
|
1513
|
-
}
|
1514
|
-
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
1515
|
-
volatile VALUE rs;
|
1516
|
-
const char *s;
|
1517
|
-
int len;
|
1518
|
-
|
1519
|
-
rs = rb_funcall(obj, oj_to_json_id, 0);
|
1520
|
-
s = rb_string_value_ptr((VALUE*)&rs);
|
1521
|
-
len = (int)RSTRING_LEN(rs);
|
1522
|
-
|
1523
|
-
if (out->end - out->cur <= len + 1) {
|
1524
|
-
grow(out, len);
|
1525
|
-
}
|
1526
|
-
memcpy(out->cur, s, len);
|
1527
|
-
out->cur += len;
|
1528
|
-
*out->cur = '\0';
|
1529
|
-
} else {
|
1530
|
-
VALUE clas = rb_obj_class(obj);
|
1531
|
-
|
1532
|
-
if (oj_bigdecimal_class == clas) {
|
1533
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1534
|
-
|
1535
|
-
if (Yes == out->opts->bigdec_as_num) {
|
1536
|
-
dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
|
1537
|
-
} else {
|
1538
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1539
|
-
}
|
1540
|
-
#if (defined T_RATIONAL && defined RRATIONAL)
|
1541
|
-
} else if (oj_datetime_class == clas || oj_date_class == clas || rb_cRational == clas) {
|
1542
|
-
#else
|
1543
|
-
} else if (oj_datetime_class == clas || oj_date_class == clas) {
|
1544
|
-
#endif
|
1545
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1546
|
-
|
1547
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1548
|
-
} else {
|
1549
|
-
dump_obj_attrs(obj, Qundef, 0, depth, out);
|
1550
|
-
}
|
1551
|
-
}
|
1552
|
-
*out->cur = '\0';
|
1553
|
-
}
|
1554
|
-
|
1555
|
-
inline static void
|
1556
|
-
dump_obj_obj(VALUE obj, int depth, Out out) {
|
1557
|
-
long id = check_circular(obj, out);
|
1558
|
-
|
1559
|
-
if (0 <= id) {
|
1560
|
-
VALUE clas = rb_obj_class(obj);
|
1561
|
-
|
1562
|
-
if (oj_bigdecimal_class == clas) {
|
1563
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1564
|
-
|
1565
|
-
dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
|
1566
|
-
} else {
|
1567
|
-
dump_obj_attrs(obj, clas, id, depth, out);
|
1568
|
-
}
|
1569
|
-
}
|
1570
|
-
}
|
1571
|
-
|
1572
|
-
#ifdef RUBINIUS_RUBY
|
1573
|
-
static int
|
1574
|
-
isRbxHashAttr(const char *attr) {
|
1575
|
-
const char *hashAttrs[] = {
|
1576
|
-
"@capacity",
|
1577
|
-
"@max_entries",
|
1578
|
-
"@state",
|
1579
|
-
"@mask",
|
1580
|
-
"@size",
|
1581
|
-
"@entries",
|
1582
|
-
"@default_proc",
|
1583
|
-
0 };
|
1584
|
-
const char **ap;
|
1585
|
-
|
1586
|
-
for (ap = hashAttrs; 0 != *ap; ap++) {
|
1587
|
-
if (0 == strcmp(attr, *ap)) {
|
1588
|
-
return 1;
|
1589
|
-
}
|
1590
|
-
}
|
1591
|
-
return 0;
|
1592
|
-
}
|
1593
|
-
#endif
|
1594
|
-
|
1595
|
-
#if HAS_IVAR_HELPERS
|
1596
|
-
static int
|
1597
|
-
dump_attr_cb(ID key, VALUE value, Out out) {
|
1598
|
-
int depth = out->depth;
|
1599
|
-
size_t size = depth * out->indent + 1;
|
1600
|
-
const char *attr = rb_id2name(key);
|
1601
|
-
|
1602
|
-
if (out->omit_nil && Qnil == value) {
|
1603
|
-
return ST_CONTINUE;
|
1604
|
-
}
|
1605
|
-
#if HAS_EXCEPTION_MAGIC
|
1606
|
-
if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
|
1607
|
-
return ST_CONTINUE;
|
1608
|
-
}
|
1609
|
-
#endif
|
1610
|
-
if (out->end - out->cur <= (long)size) {
|
1611
|
-
grow(out, size);
|
1612
|
-
}
|
1613
|
-
fill_indent(out, depth);
|
1614
|
-
if ('@' == *attr) {
|
1615
|
-
attr++;
|
1616
|
-
dump_cstr(attr, strlen(attr), 0, 0, out);
|
1617
|
-
} else {
|
1618
|
-
char buf[32];
|
1619
|
-
|
1620
|
-
*buf = '~';
|
1621
|
-
strncpy(buf + 1, attr, sizeof(buf) - 2);
|
1622
|
-
buf[sizeof(buf) - 1] = '\0';
|
1623
|
-
dump_cstr(buf, strlen(buf), 0, 0, out);
|
1624
|
-
}
|
1625
|
-
*out->cur++ = ':';
|
1626
|
-
dump_val(value, depth, out, 0, 0, true);
|
1627
|
-
out->depth = depth;
|
1628
|
-
*out->cur++ = ',';
|
1629
|
-
|
1630
|
-
return ST_CONTINUE;
|
1631
|
-
}
|
1632
|
-
#endif
|
1633
|
-
|
1634
|
-
static void
|
1635
|
-
dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
1636
|
-
size_t size = 0;
|
1637
|
-
int d2 = depth + 1;
|
1638
|
-
int type = rb_type(obj);
|
1639
|
-
|
1640
|
-
if (out->end - out->cur <= 2) {
|
1641
|
-
grow(out, 2);
|
1642
|
-
}
|
1643
|
-
*out->cur++ = '{';
|
1644
|
-
if (Qundef != clas) {
|
1645
|
-
const char *class_name = rb_class2name(clas);
|
1646
|
-
int clen = (int)strlen(class_name);
|
1647
|
-
|
1648
|
-
size = d2 * out->indent + clen + 10;
|
1649
|
-
if (out->end - out->cur <= (long)size) {
|
1650
|
-
grow(out, size);
|
1651
|
-
}
|
1652
|
-
fill_indent(out, d2);
|
1653
|
-
*out->cur++ = '"';
|
1654
|
-
*out->cur++ = '^';
|
1655
|
-
*out->cur++ = 'o';
|
1656
|
-
*out->cur++ = '"';
|
1657
|
-
*out->cur++ = ':';
|
1658
|
-
dump_cstr(class_name, clen, 0, 0, out);
|
1659
|
-
}
|
1660
|
-
if (0 < id) {
|
1661
|
-
size = d2 * out->indent + 16;
|
1662
|
-
if (out->end - out->cur <= (long)size) {
|
1663
|
-
grow(out, size);
|
1664
|
-
}
|
1665
|
-
*out->cur++ = ',';
|
1666
|
-
fill_indent(out, d2);
|
1667
|
-
*out->cur++ = '"';
|
1668
|
-
*out->cur++ = '^';
|
1669
|
-
*out->cur++ = 'i';
|
1670
|
-
*out->cur++ = '"';
|
1671
|
-
*out->cur++ = ':';
|
1672
|
-
dump_ulong(id, out);
|
1673
|
-
}
|
1674
|
-
switch (type) {
|
1675
|
-
case T_STRING:
|
1676
|
-
size = d2 * out->indent + 14;
|
1677
|
-
if (out->end - out->cur <= (long)size) {
|
1678
|
-
grow(out, size);
|
1679
|
-
}
|
1680
|
-
*out->cur++ = ',';
|
1681
|
-
fill_indent(out, d2);
|
1682
|
-
*out->cur++ = '"';
|
1683
|
-
*out->cur++ = 's';
|
1684
|
-
*out->cur++ = 'e';
|
1685
|
-
*out->cur++ = 'l';
|
1686
|
-
*out->cur++ = 'f';
|
1687
|
-
*out->cur++ = '"';
|
1688
|
-
*out->cur++ = ':';
|
1689
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&obj), (int)RSTRING_LEN(obj), 0, 0, out);
|
1690
|
-
break;
|
1691
|
-
case T_ARRAY:
|
1692
|
-
size = d2 * out->indent + 14;
|
1693
|
-
if (out->end - out->cur <= (long)size) {
|
1694
|
-
grow(out, size);
|
1695
|
-
}
|
1696
|
-
*out->cur++ = ',';
|
1697
|
-
fill_indent(out, d2);
|
1698
|
-
*out->cur++ = '"';
|
1699
|
-
*out->cur++ = 's';
|
1700
|
-
*out->cur++ = 'e';
|
1701
|
-
*out->cur++ = 'l';
|
1702
|
-
*out->cur++ = 'f';
|
1703
|
-
*out->cur++ = '"';
|
1704
|
-
*out->cur++ = ':';
|
1705
|
-
dump_array(obj, Qundef, depth + 1, out);
|
1706
|
-
break;
|
1707
|
-
case T_HASH:
|
1708
|
-
size = d2 * out->indent + 14;
|
1709
|
-
if (out->end - out->cur <= (long)size) {
|
1710
|
-
grow(out, size);
|
1711
|
-
}
|
1712
|
-
*out->cur++ = ',';
|
1713
|
-
fill_indent(out, d2);
|
1714
|
-
*out->cur++ = '"';
|
1715
|
-
*out->cur++ = 's';
|
1716
|
-
*out->cur++ = 'e';
|
1717
|
-
*out->cur++ = 'l';
|
1718
|
-
*out->cur++ = 'f';
|
1719
|
-
*out->cur++ = '"';
|
1720
|
-
*out->cur++ = ':';
|
1721
|
-
dump_hash(obj, Qundef, depth + 1, out->opts->mode, out);
|
1722
|
-
break;
|
1723
|
-
default:
|
1724
|
-
break;
|
1725
|
-
}
|
1726
|
-
{
|
1727
|
-
int cnt;
|
1728
|
-
#if HAS_IVAR_HELPERS
|
1729
|
-
cnt = (int)rb_ivar_count(obj);
|
1730
|
-
#else
|
1731
|
-
volatile VALUE vars = rb_funcall2(obj, oj_instance_variables_id, 0, 0);
|
1732
|
-
VALUE *np = RARRAY_PTR(vars);
|
1733
|
-
ID vid;
|
1734
|
-
const char *attr;
|
1735
|
-
int i;
|
1736
|
-
int first = 1;
|
1737
|
-
|
1738
|
-
cnt = (int)RARRAY_LEN(vars);
|
1739
|
-
#endif
|
1740
|
-
if (Qundef != clas && 0 < cnt) {
|
1741
|
-
*out->cur++ = ',';
|
1742
|
-
}
|
1743
|
-
out->depth = depth + 1;
|
1744
|
-
#if HAS_IVAR_HELPERS
|
1745
|
-
rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
|
1746
|
-
if (',' == *(out->cur - 1)) {
|
1747
|
-
out->cur--; // backup to overwrite last comma
|
1748
|
-
}
|
1749
|
-
#else
|
1750
|
-
size = d2 * out->indent + 1;
|
1751
|
-
for (i = cnt; 0 < i; i--, np++) {
|
1752
|
-
VALUE value;
|
1753
|
-
|
1754
|
-
vid = rb_to_id(*np);
|
1755
|
-
attr = rb_id2name(vid);
|
1756
|
-
#ifdef RUBINIUS_RUBY
|
1757
|
-
if (T_HASH == type && isRbxHashAttr(attr)) {
|
1758
|
-
continue;
|
1759
|
-
}
|
1760
|
-
#endif
|
1761
|
-
value = rb_ivar_get(obj, vid);
|
1762
|
-
if (out->omit_nil && Qnil == value) {
|
1763
|
-
continue;
|
1764
|
-
}
|
1765
|
-
if (first) {
|
1766
|
-
first = 0;
|
1767
|
-
} else {
|
1768
|
-
*out->cur++ = ',';
|
1769
|
-
}
|
1770
|
-
if (out->end - out->cur <= (long)size) {
|
1771
|
-
grow(out, size);
|
1772
|
-
}
|
1773
|
-
fill_indent(out, d2);
|
1774
|
-
if ('@' == *attr) {
|
1775
|
-
attr++;
|
1776
|
-
dump_cstr(attr, strlen(attr), 0, 0, out);
|
1777
|
-
} else {
|
1778
|
-
char buf[32];
|
1779
|
-
|
1780
|
-
*buf = '~';
|
1781
|
-
strncpy(buf + 1, attr, sizeof(buf) - 2);
|
1782
|
-
buf[sizeof(buf) - 1] = '\0';
|
1783
|
-
dump_cstr(buf, strlen(attr) + 1, 0, 0, out);
|
1784
|
-
}
|
1785
|
-
*out->cur++ = ':';
|
1786
|
-
dump_val(value, d2, out, 0, 0, true);
|
1787
|
-
if (out->end - out->cur <= 2) {
|
1788
|
-
grow(out, 2);
|
1789
|
-
}
|
1790
|
-
}
|
1791
|
-
#endif
|
1792
|
-
#if HAS_EXCEPTION_MAGIC
|
1793
|
-
if (rb_obj_is_kind_of(obj, rb_eException)) {
|
1794
|
-
volatile VALUE rv;
|
1795
|
-
|
1796
|
-
if (',' != *(out->cur - 1)) {
|
1797
|
-
*out->cur++ = ',';
|
1798
|
-
}
|
1799
|
-
// message
|
1800
|
-
if (out->end - out->cur <= (long)size) {
|
1801
|
-
grow(out, size);
|
1802
|
-
}
|
1803
|
-
fill_indent(out, d2);
|
1804
|
-
dump_cstr("~mesg", 5, 0, 0, out);
|
1805
|
-
*out->cur++ = ':';
|
1806
|
-
rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
|
1807
|
-
dump_val(rv, d2, out, 0, 0, true);
|
1808
|
-
if (out->end - out->cur <= 2) {
|
1809
|
-
grow(out, 2);
|
1810
|
-
}
|
1811
|
-
*out->cur++ = ',';
|
1812
|
-
// backtrace
|
1813
|
-
if (out->end - out->cur <= (long)size) {
|
1814
|
-
grow(out, size);
|
1815
|
-
}
|
1816
|
-
fill_indent(out, d2);
|
1817
|
-
dump_cstr("~bt", 3, 0, 0, out);
|
1818
|
-
*out->cur++ = ':';
|
1819
|
-
rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
|
1820
|
-
dump_val(rv, d2, out, 0, 0, true);
|
1821
|
-
if (out->end - out->cur <= 2) {
|
1822
|
-
grow(out, 2);
|
1823
|
-
}
|
1824
|
-
}
|
1825
|
-
#endif
|
1826
|
-
out->depth = depth;
|
1827
|
-
}
|
1828
|
-
fill_indent(out, depth);
|
1829
|
-
*out->cur++ = '}';
|
1830
|
-
*out->cur = '\0';
|
1831
|
-
}
|
1832
|
-
|
1833
|
-
static void
|
1834
|
-
dump_struct_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
|
1835
|
-
if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
|
1836
|
-
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
|
1837
|
-
|
1838
|
-
if (T_HASH != rb_type(h)) {
|
1839
|
-
// It seems that ActiveRecord implemented to_hash so that it returns
|
1840
|
-
// an Array and not a Hash. To get around that any value returned
|
1841
|
-
// will be dumped.
|
1842
|
-
|
1843
|
-
//rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
1844
|
-
dump_val(h, depth, out, 0, 0, false);
|
1845
|
-
}
|
1846
|
-
dump_hash(h, Qundef, depth, out->opts->mode, out);
|
1847
|
-
} else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
|
1848
|
-
volatile VALUE aj;
|
1849
|
-
|
1850
|
-
#if HAS_METHOD_ARITY
|
1851
|
-
// Some classes elect to not take an options argument so check the arity
|
1852
|
-
// of as_json.
|
1853
|
-
switch (rb_obj_method_arity(obj, oj_as_json_id)) {
|
1854
|
-
case 0:
|
1855
|
-
aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
|
1856
|
-
break;
|
1857
|
-
case 1:
|
1858
|
-
if (1 <= argc) {
|
1859
|
-
aj = rb_funcall2(obj, oj_as_json_id, 1, argv);
|
1860
|
-
} else {
|
1861
|
-
VALUE nothing [1];
|
1862
|
-
|
1863
|
-
nothing[0] = Qnil;
|
1864
|
-
aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
|
1865
|
-
}
|
1866
|
-
break;
|
1867
|
-
default:
|
1868
|
-
aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
|
1869
|
-
break;
|
1870
|
-
}
|
1871
|
-
#else
|
1872
|
-
aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
|
1873
|
-
#endif
|
1874
|
-
// Catch the obvious brain damaged recursive dumping.
|
1875
|
-
if (aj == obj) {
|
1876
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1877
|
-
|
1878
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1879
|
-
} else {
|
1880
|
-
dump_val(aj, depth, out, 0, 0, false);
|
1881
|
-
}
|
1882
|
-
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
1883
|
-
volatile VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
|
1884
|
-
const char *s;
|
1885
|
-
int len;
|
1886
|
-
|
1887
|
-
s = rb_string_value_ptr((VALUE*)&rs);
|
1888
|
-
len = (int)RSTRING_LEN(rs);
|
1889
|
-
if (out->end - out->cur <= len) {
|
1890
|
-
grow(out, len);
|
1891
|
-
}
|
1892
|
-
memcpy(out->cur, s, len);
|
1893
|
-
out->cur += len;
|
1894
|
-
} else {
|
1895
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1896
|
-
|
1897
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
1898
|
-
}
|
1899
|
-
}
|
1900
|
-
|
1901
|
-
static void
|
1902
|
-
dump_struct_obj(VALUE obj, int depth, Out out) {
|
1903
|
-
VALUE clas = rb_obj_class(obj);
|
1904
|
-
const char *class_name = rb_class2name(clas);
|
1905
|
-
int i;
|
1906
|
-
int d2 = depth + 1;
|
1907
|
-
int d3 = d2 + 1;
|
1908
|
-
size_t len = strlen(class_name);
|
1909
|
-
size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
|
1910
|
-
|
1911
|
-
if (out->end - out->cur <= (long)size) {
|
1912
|
-
grow(out, size);
|
1913
|
-
}
|
1914
|
-
*out->cur++ = '{';
|
1915
|
-
fill_indent(out, d2);
|
1916
|
-
*out->cur++ = '"';
|
1917
|
-
*out->cur++ = '^';
|
1918
|
-
*out->cur++ = 'u';
|
1919
|
-
*out->cur++ = '"';
|
1920
|
-
*out->cur++ = ':';
|
1921
|
-
*out->cur++ = '[';
|
1922
|
-
#if HAS_STRUCT_MEMBERS
|
1923
|
-
if ('#' == *class_name) {
|
1924
|
-
VALUE ma = rb_struct_s_members(clas);
|
1925
|
-
const char *name;
|
1926
|
-
int cnt = (int)RARRAY_LEN(ma);
|
1927
|
-
|
1928
|
-
*out->cur++ = '[';
|
1929
|
-
for (i = 0; i < cnt; i++) {
|
1930
|
-
name = rb_id2name(SYM2ID(rb_ary_entry(ma, i)));
|
1931
|
-
len = strlen(name);
|
1932
|
-
size = len + 3;
|
1933
|
-
if (out->end - out->cur <= (long)size) {
|
1934
|
-
grow(out, size);
|
1935
|
-
}
|
1936
|
-
if (0 < i) {
|
1937
|
-
*out->cur++ = ',';
|
1938
|
-
}
|
1939
|
-
*out->cur++ = '"';
|
1940
|
-
memcpy(out->cur, name, len);
|
1941
|
-
out->cur += len;
|
1942
|
-
*out->cur++ = '"';
|
1943
|
-
}
|
1944
|
-
*out->cur++ = ']';
|
1945
|
-
} else {
|
1946
|
-
#else
|
1947
|
-
if (true) {
|
1948
|
-
#endif
|
1949
|
-
fill_indent(out, d3);
|
1950
|
-
*out->cur++ = '"';
|
1951
|
-
memcpy(out->cur, class_name, len);
|
1952
|
-
out->cur += len;
|
1953
|
-
*out->cur++ = '"';
|
1954
|
-
}
|
1955
|
-
*out->cur++ = ',';
|
1956
|
-
size = d3 * out->indent + 2;
|
1957
|
-
#ifdef RSTRUCT_LEN
|
1958
|
-
{
|
1959
|
-
VALUE v;
|
1960
|
-
int cnt;
|
1961
|
-
#if UNIFY_FIXNUM_AND_BIGNUM
|
1962
|
-
cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
|
1963
|
-
#else // UNIFY_FIXNUM_AND_INTEGER
|
1964
|
-
cnt = (int)RSTRUCT_LEN(obj);
|
1965
|
-
#endif // UNIFY_FIXNUM_AND_INTEGER
|
1966
|
-
|
1967
|
-
for (i = 0; i < cnt; i++) {
|
1968
|
-
v = RSTRUCT_GET(obj, i);
|
1969
|
-
if (out->end - out->cur <= (long)size) {
|
1970
|
-
grow(out, size);
|
1971
|
-
}
|
1972
|
-
fill_indent(out, d3);
|
1973
|
-
dump_val(v, d3, out, 0, 0, true);
|
1974
|
-
*out->cur++ = ',';
|
1975
|
-
}
|
1976
|
-
}
|
1977
|
-
#else
|
1978
|
-
{
|
1979
|
-
// This is a bit risky as a struct in C ruby is not the same as a Struct
|
1980
|
-
// class in interpreted Ruby so length() may not be defined.
|
1981
|
-
int slen = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
|
1982
|
-
|
1983
|
-
for (i = 0; i < slen; i++) {
|
1984
|
-
if (out->end - out->cur <= (long)size) {
|
1985
|
-
grow(out, size);
|
1986
|
-
}
|
1987
|
-
fill_indent(out, d3);
|
1988
|
-
dump_val(rb_struct_aref(obj, INT2FIX(i)), d3, out, 0, 0, true);
|
1989
|
-
*out->cur++ = ',';
|
1990
|
-
}
|
1991
|
-
}
|
1992
|
-
#endif
|
1993
|
-
out->cur--;
|
1994
|
-
*out->cur++ = ']';
|
1995
|
-
*out->cur++ = '}';
|
1996
|
-
*out->cur = '\0';
|
1997
|
-
}
|
1998
|
-
|
1999
|
-
static void
|
2000
|
-
dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
|
2001
|
-
ID *idp;
|
2002
|
-
AttrGetFunc *fp;
|
2003
|
-
volatile VALUE v;
|
2004
|
-
const char *name;
|
2005
|
-
size_t size;
|
2006
|
-
int d2 = depth + 1;
|
2007
|
-
|
2008
|
-
if (out->end - out->cur <= 2) {
|
2009
|
-
grow(out, 2);
|
2010
|
-
}
|
2011
|
-
*out->cur++ = '{';
|
2012
|
-
if (Qundef != clas) {
|
2013
|
-
const char *class_name = rb_class2name(clas);
|
2014
|
-
int clen = (int)strlen(class_name);
|
2015
|
-
|
2016
|
-
size = d2 * out->indent + clen + 10;
|
2017
|
-
if (out->end - out->cur <= (long)size) {
|
2018
|
-
grow(out, size);
|
2019
|
-
}
|
2020
|
-
fill_indent(out, d2);
|
2021
|
-
*out->cur++ = '"';
|
2022
|
-
*out->cur++ = '^';
|
2023
|
-
*out->cur++ = 'O';
|
2024
|
-
*out->cur++ = '"';
|
2025
|
-
*out->cur++ = ':';
|
2026
|
-
dump_cstr(class_name, clen, 0, 0, out);
|
2027
|
-
*out->cur++ = ',';
|
2028
|
-
}
|
2029
|
-
if (odd->raw) {
|
2030
|
-
v = rb_funcall(obj, *odd->attrs, 0);
|
2031
|
-
if (Qundef == v || T_STRING != rb_type(v)) {
|
2032
|
-
rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
|
2033
|
-
} else {
|
2034
|
-
const char *s = rb_string_value_ptr((VALUE*)&v);
|
2035
|
-
int len = (int)RSTRING_LEN(v);
|
2036
|
-
const char *name = rb_id2name(*odd->attrs);
|
2037
|
-
size_t nlen = strlen(name);
|
2038
|
-
|
2039
|
-
size = len + d2 * out->indent + nlen + 10;
|
2040
|
-
if (out->end - out->cur <= (long)size) {
|
2041
|
-
grow(out, size);
|
2042
|
-
}
|
2043
|
-
fill_indent(out, d2);
|
2044
|
-
*out->cur++ = '"';
|
2045
|
-
memcpy(out->cur, name, nlen);
|
2046
|
-
out->cur += nlen;
|
2047
|
-
*out->cur++ = '"';
|
2048
|
-
*out->cur++ = ':';
|
2049
|
-
memcpy(out->cur, s, len);
|
2050
|
-
out->cur += len;
|
2051
|
-
*out->cur = '\0';
|
2052
|
-
}
|
693
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
|
694
|
+
int len;
|
695
|
+
|
696
|
+
if (9 > out->opts->sec_prec) {
|
697
|
+
format[32] = '0' + out->opts->sec_prec;
|
698
|
+
}
|
699
|
+
len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
|
700
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
2053
701
|
} else {
|
2054
|
-
|
2055
|
-
|
2056
|
-
size_t nlen;
|
2057
|
-
|
2058
|
-
if (out->end - out->cur <= (long)size) {
|
2059
|
-
grow(out, size);
|
2060
|
-
}
|
2061
|
-
name = rb_id2name(*idp);
|
2062
|
-
nlen = strlen(name);
|
2063
|
-
if (0 != *fp) {
|
2064
|
-
v = (*fp)(obj);
|
2065
|
-
} else if (0 == strchr(name, '.')) {
|
2066
|
-
v = rb_funcall(obj, *idp, 0);
|
2067
|
-
} else {
|
2068
|
-
char nbuf[256];
|
2069
|
-
char *n2 = nbuf;
|
2070
|
-
char *n;
|
2071
|
-
char *end;
|
2072
|
-
ID i;
|
2073
|
-
|
2074
|
-
if (sizeof(nbuf) <= nlen) {
|
2075
|
-
n2 = strdup(name);
|
2076
|
-
} else {
|
2077
|
-
strcpy(n2, name);
|
2078
|
-
}
|
2079
|
-
n = n2;
|
2080
|
-
v = obj;
|
2081
|
-
while (0 != (end = strchr(n, '.'))) {
|
2082
|
-
*end = '\0';
|
2083
|
-
i = rb_intern(n);
|
2084
|
-
v = rb_funcall(v, i, 0);
|
2085
|
-
n = end + 1;
|
2086
|
-
}
|
2087
|
-
i = rb_intern(n);
|
2088
|
-
v = rb_funcall(v, i, 0);
|
2089
|
-
if (nbuf != n2) {
|
2090
|
-
free(n2);
|
2091
|
-
}
|
2092
|
-
}
|
2093
|
-
fill_indent(out, d2);
|
2094
|
-
dump_cstr(name, nlen, 0, 0, out);
|
2095
|
-
*out->cur++ = ':';
|
2096
|
-
dump_val(v, d2, out, 0, 0, true);
|
2097
|
-
if (out->end - out->cur <= 2) {
|
2098
|
-
grow(out, 2);
|
2099
|
-
}
|
2100
|
-
*out->cur++ = ',';
|
2101
|
-
}
|
2102
|
-
out->cur--;
|
2103
|
-
}
|
2104
|
-
*out->cur++ = '}';
|
2105
|
-
*out->cur = '\0';
|
2106
|
-
}
|
702
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
|
703
|
+
int len;
|
2107
704
|
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2113
|
-
static void
|
2114
|
-
dump_val(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
|
2115
|
-
int type = rb_type(obj);
|
2116
|
-
|
2117
|
-
if (MAX_DEPTH < depth) {
|
2118
|
-
rb_raise(rb_eNoMemError, "Too deeply nested.\n");
|
2119
|
-
}
|
2120
|
-
#ifdef OJ_DEBUG
|
2121
|
-
printf("Oj-debug: dump_val %s\n", rb_class2name(rb_obj_class(obj)));
|
2122
|
-
#endif
|
2123
|
-
switch (type) {
|
2124
|
-
case T_NIL: dump_nil(out); break;
|
2125
|
-
case T_TRUE: dump_true(out); break;
|
2126
|
-
case T_FALSE: dump_false(out); break;
|
2127
|
-
case T_FIXNUM: dump_fixnum(obj, out); break;
|
2128
|
-
case T_FLOAT: dump_float(obj, out); break;
|
2129
|
-
case T_MODULE:
|
2130
|
-
case T_CLASS:
|
2131
|
-
switch (out->opts->mode) {
|
2132
|
-
case StrictMode: raise_strict(obj); break;
|
2133
|
-
case NullMode: dump_nil(out); break;
|
2134
|
-
case CompatMode: dump_class_comp(obj, out); break;
|
2135
|
-
case ObjectMode:
|
2136
|
-
default: dump_class_obj(obj, out); break;
|
2137
|
-
}
|
2138
|
-
break;
|
2139
|
-
case T_SYMBOL:
|
2140
|
-
switch (out->opts->mode) {
|
2141
|
-
case StrictMode: raise_strict(obj); break;
|
2142
|
-
case NullMode: dump_nil(out); break;
|
2143
|
-
case CompatMode: dump_sym_comp(obj, out); break;
|
2144
|
-
case ObjectMode:
|
2145
|
-
default: dump_sym_obj(obj, out); break;
|
2146
|
-
}
|
2147
|
-
break;
|
2148
|
-
case T_STRUCT: // for Range
|
2149
|
-
switch (out->opts->mode) {
|
2150
|
-
case StrictMode: raise_strict(obj); break;
|
2151
|
-
case NullMode: dump_nil(out); break;
|
2152
|
-
case CompatMode: dump_struct_comp(obj, depth, out, argc, argv, as_ok); break;
|
2153
|
-
case ObjectMode:
|
2154
|
-
default: dump_struct_obj(obj, depth, out); break;
|
2155
|
-
}
|
2156
|
-
break;
|
2157
|
-
default:
|
2158
|
-
// Most developers have enough sense not to subclass primitive types but
|
2159
|
-
// since these classes could potentially be subclassed a check for odd
|
2160
|
-
// classes is performed.
|
2161
|
-
{
|
2162
|
-
VALUE clas = rb_obj_class(obj);
|
2163
|
-
Odd odd;
|
2164
|
-
|
2165
|
-
if (ObjectMode == out->opts->mode && 0 != (odd = oj_get_odd(clas))) {
|
2166
|
-
dump_odd(obj, odd, clas, depth + 1, out);
|
2167
|
-
return;
|
2168
|
-
}
|
2169
|
-
switch (type) {
|
2170
|
-
case T_BIGNUM: dump_bignum(obj, out); break;
|
2171
|
-
case T_STRING:
|
2172
|
-
switch (out->opts->mode) {
|
2173
|
-
case StrictMode:
|
2174
|
-
case NullMode:
|
2175
|
-
case CompatMode: dump_str_comp(obj, out); break;
|
2176
|
-
case ObjectMode:
|
2177
|
-
default: dump_str_obj(obj, clas, depth, out); break;
|
2178
|
-
}
|
2179
|
-
break;
|
2180
|
-
case T_ARRAY: dump_array(obj, clas, depth, out); break;
|
2181
|
-
case T_HASH: dump_hash(obj, clas, depth, out->opts->mode, out); break;
|
2182
|
-
#if (defined T_RATIONAL && defined RRATIONAL)
|
2183
|
-
case T_RATIONAL:
|
2184
|
-
#endif
|
2185
|
-
case T_OBJECT:
|
2186
|
-
switch (out->opts->mode) {
|
2187
|
-
case StrictMode: dump_data_strict(obj, out); break;
|
2188
|
-
case NullMode: dump_data_null(obj, out); break;
|
2189
|
-
case CompatMode: dump_obj_comp(obj, depth, out, argc, argv, as_ok); break;
|
2190
|
-
case ObjectMode:
|
2191
|
-
default: dump_obj_obj(obj, depth, out); break;
|
2192
|
-
}
|
2193
|
-
break;
|
2194
|
-
case T_DATA:
|
2195
|
-
switch (out->opts->mode) {
|
2196
|
-
case StrictMode: dump_data_strict(obj, out); break;
|
2197
|
-
case NullMode: dump_data_null(obj, out); break;
|
2198
|
-
case CompatMode: dump_data_comp(obj, depth, out, argc, argv, as_ok);break;
|
2199
|
-
case ObjectMode:
|
2200
|
-
default: dump_data_obj(obj, depth, out); break;
|
2201
|
-
}
|
2202
|
-
break;
|
2203
|
-
#if (defined T_COMPLEX && defined RCOMPLEX)
|
2204
|
-
case T_COMPLEX:
|
2205
|
-
#endif
|
2206
|
-
case T_REGEXP:
|
2207
|
-
switch (out->opts->mode) {
|
2208
|
-
case StrictMode: raise_strict(obj); break;
|
2209
|
-
case NullMode: dump_nil(out); break;
|
2210
|
-
case CompatMode:
|
2211
|
-
case ObjectMode:
|
2212
|
-
default: dump_obj_comp(obj, depth, out, argc, argv, as_ok); break;
|
2213
|
-
}
|
2214
|
-
break;
|
2215
|
-
default:
|
2216
|
-
switch (out->opts->mode) {
|
2217
|
-
case StrictMode: raise_strict(obj); break;
|
2218
|
-
case NullMode: dump_nil(out); break;
|
2219
|
-
case CompatMode: {
|
2220
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
2221
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
2222
|
-
break;
|
2223
|
-
}
|
2224
|
-
case ObjectMode:
|
2225
|
-
default:
|
2226
|
-
rb_raise(rb_eNotImpError, "Failed to dump '%s' Object (%02x)\n",
|
2227
|
-
rb_class2name(rb_obj_class(obj)), rb_type(obj));
|
2228
|
-
break;
|
2229
|
-
}
|
2230
|
-
break;
|
2231
|
-
}
|
2232
|
-
}
|
705
|
+
if (9 > out->opts->sec_prec) {
|
706
|
+
format[32] = '0' + out->opts->sec_prec;
|
707
|
+
}
|
708
|
+
len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
|
709
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
2233
710
|
}
|
2234
711
|
}
|
2235
712
|
|
2236
|
-
void
|
2237
|
-
oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
|
713
|
+
void oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
|
2238
714
|
oj_dump_obj_to_json_using_params(obj, copts, out, 0, 0);
|
2239
715
|
}
|
2240
716
|
|
2241
|
-
void
|
2242
|
-
oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
|
717
|
+
void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
|
2243
718
|
if (0 == out->buf) {
|
2244
|
-
|
2245
|
-
out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
|
2246
|
-
out->allocated = 1;
|
719
|
+
oj_out_init(out);
|
2247
720
|
}
|
2248
|
-
out->cur = out->buf;
|
2249
721
|
out->circ_cnt = 0;
|
2250
|
-
out->opts
|
722
|
+
out->opts = copts;
|
2251
723
|
out->hash_cnt = 0;
|
724
|
+
out->indent = copts->indent;
|
725
|
+
out->argc = argc;
|
726
|
+
out->argv = argv;
|
727
|
+
out->ropts = NULL;
|
2252
728
|
if (Yes == copts->circular) {
|
2253
|
-
|
729
|
+
oj_cache8_new(&out->circ_cache);
|
730
|
+
}
|
731
|
+
switch (copts->mode) {
|
732
|
+
case StrictMode: oj_dump_strict_val(obj, 0, out); break;
|
733
|
+
case NullMode: oj_dump_null_val(obj, 0, out); break;
|
734
|
+
case ObjectMode: oj_dump_obj_val(obj, 0, out); break;
|
735
|
+
case CompatMode: oj_dump_compat_val(obj, 0, out, Yes == copts->to_json); break;
|
736
|
+
case RailsMode: oj_dump_rails_val(obj, 0, out); break;
|
737
|
+
case CustomMode: oj_dump_custom_val(obj, 0, out, true); break;
|
738
|
+
case WabMode: oj_dump_wab_val(obj, 0, out); break;
|
739
|
+
default: oj_dump_custom_val(obj, 0, out, true); break;
|
2254
740
|
}
|
2255
|
-
out->indent = copts->indent;
|
2256
|
-
dump_val(obj, 0, out, argc, argv, true);
|
2257
741
|
if (0 < out->indent) {
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
default:
|
2264
|
-
break;
|
2265
|
-
}
|
742
|
+
switch (*(out->cur - 1)) {
|
743
|
+
case ']':
|
744
|
+
case '}': assure_size(out, 1); *out->cur++ = '\n';
|
745
|
+
default: break;
|
746
|
+
}
|
2266
747
|
}
|
2267
748
|
*out->cur = '\0';
|
2268
749
|
if (Yes == copts->circular) {
|
2269
|
-
|
750
|
+
oj_cache8_delete(out->circ_cache);
|
2270
751
|
}
|
2271
752
|
}
|
2272
753
|
|
2273
|
-
void
|
2274
|
-
|
2275
|
-
|
2276
|
-
|
2277
|
-
|
2278
|
-
|
2279
|
-
|
2280
|
-
|
2281
|
-
out.buf = buf;
|
2282
|
-
out.end = buf + sizeof(buf) - BUFFER_EXTRA;
|
2283
|
-
out.allocated = 0;
|
754
|
+
void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
|
755
|
+
struct _out out;
|
756
|
+
size_t size;
|
757
|
+
FILE *f;
|
758
|
+
int ok;
|
759
|
+
|
760
|
+
oj_out_init(&out);
|
761
|
+
|
2284
762
|
out.omit_nil = copts->dump_opts.omit_nil;
|
2285
763
|
oj_dump_obj_to_json(obj, copts, &out);
|
2286
764
|
size = out.cur - out.buf;
|
2287
765
|
if (0 == (f = fopen(path, "w"))) {
|
2288
|
-
|
2289
|
-
|
2290
|
-
}
|
2291
|
-
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
766
|
+
oj_out_free(&out);
|
767
|
+
rb_raise(rb_eIOError, "%s", strerror(errno));
|
2292
768
|
}
|
2293
769
|
ok = (size == fwrite(out.buf, 1, size, f));
|
2294
|
-
|
2295
|
-
|
770
|
+
|
771
|
+
oj_out_free(&out);
|
772
|
+
|
773
|
+
if (!ok) {
|
774
|
+
int err = ferror(f);
|
775
|
+
fclose(f);
|
776
|
+
|
777
|
+
rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
|
2296
778
|
}
|
2297
779
|
fclose(f);
|
2298
|
-
|
2299
|
-
int err = ferror(f);
|
780
|
+
}
|
2300
781
|
|
2301
|
-
|
782
|
+
#if !IS_WINDOWS
|
783
|
+
static void write_ready(int fd) {
|
784
|
+
struct pollfd pp;
|
785
|
+
int i;
|
786
|
+
|
787
|
+
pp.fd = fd;
|
788
|
+
pp.events = POLLERR | POLLOUT;
|
789
|
+
pp.revents = 0;
|
790
|
+
if (0 >= (i = poll(&pp, 1, 5000))) {
|
791
|
+
if (0 == i || EAGAIN == errno) {
|
792
|
+
rb_raise(rb_eIOError, "write timed out");
|
793
|
+
}
|
794
|
+
rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
|
2302
795
|
}
|
2303
796
|
}
|
797
|
+
#endif
|
2304
798
|
|
2305
|
-
void
|
2306
|
-
|
2307
|
-
|
2308
|
-
|
2309
|
-
ssize_t size;
|
2310
|
-
VALUE clas = rb_obj_class(stream);
|
799
|
+
void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
|
800
|
+
struct _out out;
|
801
|
+
ssize_t size;
|
802
|
+
VALUE clas = rb_obj_class(stream);
|
2311
803
|
#if !IS_WINDOWS
|
2312
|
-
int
|
2313
|
-
VALUE
|
804
|
+
int fd;
|
805
|
+
VALUE s;
|
2314
806
|
#endif
|
2315
807
|
|
2316
|
-
out
|
2317
|
-
|
2318
|
-
out.allocated = 0;
|
808
|
+
oj_out_init(&out);
|
809
|
+
|
2319
810
|
out.omit_nil = copts->dump_opts.omit_nil;
|
2320
811
|
oj_dump_obj_to_json(obj, copts, &out);
|
2321
812
|
size = out.cur - out.buf;
|
2322
813
|
if (oj_stringio_class == clas) {
|
2323
|
-
|
814
|
+
rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
|
2324
815
|
#if !IS_WINDOWS
|
2325
|
-
} else if (rb_respond_to(stream, oj_fileno_id) &&
|
2326
|
-
|
2327
|
-
|
2328
|
-
|
2329
|
-
|
2330
|
-
|
2331
|
-
|
2332
|
-
|
2333
|
-
|
816
|
+
} else if (rb_respond_to(stream, oj_fileno_id) && Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
|
817
|
+
0 != (fd = FIX2INT(s))) {
|
818
|
+
ssize_t cnt;
|
819
|
+
ssize_t total = 0;
|
820
|
+
|
821
|
+
while (true) {
|
822
|
+
if (0 > (cnt = write(fd, out.buf + total, size - total))) {
|
823
|
+
if (EAGAIN != errno) {
|
824
|
+
rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
|
825
|
+
break;
|
826
|
+
}
|
827
|
+
}
|
828
|
+
total += cnt;
|
829
|
+
if (size <= total) {
|
830
|
+
// Completed
|
831
|
+
break;
|
832
|
+
}
|
833
|
+
write_ready(fd);
|
834
|
+
}
|
2334
835
|
#endif
|
2335
836
|
} else if (rb_respond_to(stream, oj_write_id)) {
|
2336
|
-
|
837
|
+
rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
|
2337
838
|
} else {
|
2338
|
-
|
2339
|
-
|
2340
|
-
}
|
2341
|
-
rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
|
2342
|
-
}
|
2343
|
-
if (out.allocated) {
|
2344
|
-
xfree(out.buf);
|
839
|
+
oj_out_free(&out);
|
840
|
+
rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
|
2345
841
|
}
|
842
|
+
oj_out_free(&out);
|
2346
843
|
}
|
2347
844
|
|
2348
|
-
|
845
|
+
void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
|
846
|
+
int idx = RB_ENCODING_GET(obj);
|
2349
847
|
|
2350
|
-
|
2351
|
-
|
2352
|
-
|
2353
|
-
grow(out, size);
|
848
|
+
if (oj_utf8_encoding_index != idx) {
|
849
|
+
rb_encoding *enc = rb_enc_from_index(idx);
|
850
|
+
obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
|
2354
851
|
}
|
2355
|
-
|
2356
|
-
out->cur += size;
|
2357
|
-
*out->cur = '\0';
|
852
|
+
oj_dump_cstr(RSTRING_PTR(obj), RSTRING_LEN(obj), 0, 0, out);
|
2358
853
|
}
|
2359
854
|
|
2360
|
-
|
2361
|
-
|
2362
|
-
|
2363
|
-
|
2364
|
-
dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out);
|
2365
|
-
break;
|
2366
|
-
case RUBY_VAL:
|
2367
|
-
dump_cstr(rb_string_value_cstr(&leaf->value), (int)RSTRING_LEN(leaf->value), 0, 0, out);
|
2368
|
-
break;
|
2369
|
-
case COL_VAL:
|
2370
|
-
default:
|
2371
|
-
rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
|
2372
|
-
break;
|
2373
|
-
}
|
855
|
+
void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
|
856
|
+
volatile VALUE s = rb_sym2str(obj);
|
857
|
+
|
858
|
+
oj_dump_cstr(RSTRING_PTR(s), RSTRING_LEN(s), 0, 0, out);
|
2374
859
|
}
|
2375
860
|
|
2376
|
-
static void
|
2377
|
-
|
2378
|
-
|
2379
|
-
|
2380
|
-
|
2381
|
-
|
2382
|
-
|
2383
|
-
|
2384
|
-
dump_bignum(leaf->value, out);
|
2385
|
-
} else {
|
2386
|
-
dump_fixnum(leaf->value, out);
|
2387
|
-
}
|
2388
|
-
break;
|
2389
|
-
case COL_VAL:
|
2390
|
-
default:
|
2391
|
-
rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
|
2392
|
-
break;
|
861
|
+
static void debug_raise(const char *orig, size_t cnt, int line) {
|
862
|
+
char buf[1024];
|
863
|
+
char *b = buf;
|
864
|
+
const char *s = orig;
|
865
|
+
const char *s_end = s + cnt;
|
866
|
+
|
867
|
+
if (32 < s_end - s) {
|
868
|
+
s_end = s + 32;
|
2393
869
|
}
|
870
|
+
for (; s < s_end; s++) {
|
871
|
+
b += sprintf(b, " %02x", *s);
|
872
|
+
}
|
873
|
+
*b = '\0';
|
874
|
+
rb_raise(oj_json_generator_error_class, "Partial character in string. %s @ %d", buf, line);
|
2394
875
|
}
|
2395
876
|
|
2396
|
-
|
2397
|
-
|
2398
|
-
|
2399
|
-
|
2400
|
-
|
2401
|
-
|
2402
|
-
|
2403
|
-
|
2404
|
-
|
2405
|
-
|
2406
|
-
|
2407
|
-
|
2408
|
-
|
877
|
+
void oj_dump_raw_json(VALUE obj, int depth, Out out) {
|
878
|
+
if (oj_string_writer_class == rb_obj_class(obj)) {
|
879
|
+
StrWriter sw;
|
880
|
+
size_t len;
|
881
|
+
|
882
|
+
sw = oj_str_writer_unwrap(obj);
|
883
|
+
len = sw->out.cur - sw->out.buf;
|
884
|
+
|
885
|
+
if (0 < len) {
|
886
|
+
len--;
|
887
|
+
}
|
888
|
+
oj_dump_raw(sw->out.buf, len, out);
|
889
|
+
} else {
|
890
|
+
volatile VALUE jv;
|
891
|
+
|
892
|
+
TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyIn);
|
893
|
+
jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
|
894
|
+
TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyOut);
|
895
|
+
oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
|
2409
896
|
}
|
2410
897
|
}
|
2411
898
|
|
2412
|
-
|
2413
|
-
|
2414
|
-
|
2415
|
-
|
899
|
+
#ifdef HAVE_SIMD_NEON
|
900
|
+
typedef struct _neon_match_result {
|
901
|
+
uint8x16_t needs_escape;
|
902
|
+
bool has_some_hibit;
|
903
|
+
bool do_unicode_validation;
|
904
|
+
} neon_match_result;
|
905
|
+
|
906
|
+
#if defined(__clang__) || defined(__GNUC__)
|
907
|
+
#define FORCE_INLINE __attribute__((always_inline))
|
908
|
+
#else
|
909
|
+
#define FORCE_INLINE
|
910
|
+
#endif
|
911
|
+
|
912
|
+
static inline FORCE_INLINE neon_match_result
|
913
|
+
neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool do_unicode_validation, bool has_hi) {
|
914
|
+
neon_match_result result = {.has_some_hibit = false, .do_unicode_validation = false};
|
2416
915
|
|
2417
|
-
|
2418
|
-
|
2419
|
-
|
916
|
+
uint8x16_t chunk = vld1q_u8((const unsigned char *)str);
|
917
|
+
uint8x16_t tmp1 = vqtbl4q_u8(cmap_neon[0], chunk);
|
918
|
+
uint8x16_t tmp2 = vqtbl4q_u8(cmap_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
|
919
|
+
result.needs_escape = vorrq_u8(tmp1, tmp2);
|
920
|
+
if (neon_table_size > 2) {
|
921
|
+
uint8x16_t tmp3 = vqtbl4q_u8(cmap_neon[2], veorq_u8(chunk, vdupq_n_u8(0x80)));
|
922
|
+
uint8x16_t tmp4 = vqtbl4q_u8(cmap_neon[3], veorq_u8(chunk, vdupq_n_u8(0xc0)));
|
923
|
+
result.needs_escape = vorrq_u8(result.needs_escape, vorrq_u8(tmp4, tmp3));
|
2420
924
|
}
|
2421
|
-
|
2422
|
-
|
2423
|
-
|
2424
|
-
|
2425
|
-
Leaf first = leaf->elements->next;
|
2426
|
-
Leaf e = first;
|
2427
|
-
|
2428
|
-
size = d2 * out->indent + 2;
|
2429
|
-
do {
|
2430
|
-
if (out->end - out->cur <= (long)size) {
|
2431
|
-
grow(out, size);
|
2432
|
-
}
|
2433
|
-
fill_indent(out, d2);
|
2434
|
-
dump_leaf(e, d2, out);
|
2435
|
-
if (e->next != first) {
|
2436
|
-
*out->cur++ = ',';
|
2437
|
-
}
|
2438
|
-
e = e->next;
|
2439
|
-
} while (e != first);
|
2440
|
-
size = depth * out->indent + 1;
|
2441
|
-
if (out->end - out->cur <= (long)size) {
|
2442
|
-
grow(out, size);
|
2443
|
-
}
|
2444
|
-
fill_indent(out, depth);
|
2445
|
-
*out->cur++ = ']';
|
925
|
+
if (has_hi && do_unicode_validation) {
|
926
|
+
uint8x16_t has_some_hibit = vandq_u8(chunk, vdupq_n_u8(0x80));
|
927
|
+
result.has_some_hibit = vmaxvq_u8(has_some_hibit) != 0;
|
928
|
+
result.do_unicode_validation = has_hi && do_unicode_validation && result.has_some_hibit;
|
2446
929
|
}
|
2447
|
-
|
930
|
+
return result;
|
2448
931
|
}
|
2449
932
|
|
2450
|
-
|
2451
|
-
|
2452
|
-
|
2453
|
-
|
933
|
+
#endif /* HAVE_SIMD_NEON */
|
934
|
+
|
935
|
+
void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
|
936
|
+
size_t size;
|
937
|
+
char *cmap;
|
938
|
+
#ifdef HAVE_SIMD_NEON
|
939
|
+
uint8x16x4_t *cmap_neon = NULL;
|
940
|
+
int neon_table_size;
|
941
|
+
#endif /* HAVE_SIMD_NEON */
|
942
|
+
const char *orig = str;
|
943
|
+
bool has_hi = false;
|
944
|
+
bool do_unicode_validation = false;
|
2454
945
|
|
2455
|
-
|
2456
|
-
|
2457
|
-
|
946
|
+
switch (out->opts->escape_mode) {
|
947
|
+
case NLEsc:
|
948
|
+
cmap = newline_friendly_chars;
|
949
|
+
size = newline_friendly_size((uint8_t *)str, cnt);
|
950
|
+
break;
|
951
|
+
case ASCIIEsc:
|
952
|
+
cmap = ascii_friendly_chars;
|
953
|
+
size = ascii_friendly_size((uint8_t *)str, cnt);
|
954
|
+
break;
|
955
|
+
case SlashEsc:
|
956
|
+
has_hi = true;
|
957
|
+
cmap = slash_friendly_chars;
|
958
|
+
size = slash_friendly_size((uint8_t *)str, cnt);
|
959
|
+
break;
|
960
|
+
case XSSEsc:
|
961
|
+
cmap = xss_friendly_chars;
|
962
|
+
size = xss_friendly_size((uint8_t *)str, cnt);
|
963
|
+
break;
|
964
|
+
case JXEsc:
|
965
|
+
cmap = hixss_friendly_chars;
|
966
|
+
size = hixss_friendly_size((uint8_t *)str, cnt);
|
967
|
+
do_unicode_validation = true;
|
968
|
+
break;
|
969
|
+
case RailsXEsc: {
|
970
|
+
long sz;
|
971
|
+
|
972
|
+
cmap = rails_xss_friendly_chars;
|
973
|
+
#ifdef HAVE_SIMD_NEON
|
974
|
+
cmap_neon = rails_xss_friendly_chars_neon;
|
975
|
+
neon_table_size = 4;
|
976
|
+
#endif /* HAVE_NEON_SIMD */
|
977
|
+
sz = rails_xss_friendly_size((uint8_t *)str, cnt);
|
978
|
+
if (sz < 0) {
|
979
|
+
has_hi = true;
|
980
|
+
size = (size_t)-sz;
|
981
|
+
} else {
|
982
|
+
size = (size_t)sz;
|
983
|
+
}
|
984
|
+
do_unicode_validation = true;
|
985
|
+
break;
|
986
|
+
}
|
987
|
+
case RailsEsc: {
|
988
|
+
long sz;
|
989
|
+
cmap = rails_friendly_chars;
|
990
|
+
#ifdef HAVE_SIMD_NEON
|
991
|
+
cmap_neon = rails_friendly_chars_neon;
|
992
|
+
neon_table_size = 2;
|
993
|
+
#endif /* HAVE_NEON_SIMD */
|
994
|
+
sz = rails_friendly_size((uint8_t *)str, cnt);
|
995
|
+
if (sz < 0) {
|
996
|
+
has_hi = true;
|
997
|
+
size = (size_t)-sz;
|
998
|
+
} else {
|
999
|
+
size = (size_t)sz;
|
1000
|
+
}
|
1001
|
+
do_unicode_validation = true;
|
1002
|
+
break;
|
2458
1003
|
}
|
2459
|
-
|
2460
|
-
|
2461
|
-
|
1004
|
+
case JSONEsc:
|
1005
|
+
default: cmap = hibit_friendly_chars;
|
1006
|
+
#ifdef HAVE_SIMD_NEON
|
1007
|
+
cmap_neon = hibit_friendly_chars_neon;
|
1008
|
+
neon_table_size = 2;
|
1009
|
+
#endif /* HAVE_NEON_SIMD */
|
1010
|
+
size = hibit_friendly_size((uint8_t *)str, cnt);
|
1011
|
+
}
|
1012
|
+
assure_size(out, size + BUFFER_EXTRA);
|
1013
|
+
*out->cur++ = '"';
|
1014
|
+
|
1015
|
+
if (escape1) {
|
1016
|
+
APPEND_CHARS(out->cur, "\\u00", 4);
|
1017
|
+
dump_hex((uint8_t)*str, out);
|
1018
|
+
cnt--;
|
1019
|
+
size--;
|
1020
|
+
str++;
|
1021
|
+
is_sym = 0; // just to make sure
|
1022
|
+
}
|
1023
|
+
if (cnt == size && !has_hi) {
|
1024
|
+
if (is_sym) {
|
1025
|
+
*out->cur++ = ':';
|
1026
|
+
}
|
1027
|
+
APPEND_CHARS(out->cur, str, cnt);
|
1028
|
+
*out->cur++ = '"';
|
2462
1029
|
} else {
|
2463
|
-
|
2464
|
-
|
2465
|
-
|
2466
|
-
|
2467
|
-
|
2468
|
-
|
2469
|
-
|
2470
|
-
|
2471
|
-
|
2472
|
-
|
2473
|
-
|
2474
|
-
|
2475
|
-
|
2476
|
-
|
2477
|
-
|
2478
|
-
|
2479
|
-
|
2480
|
-
|
2481
|
-
|
2482
|
-
|
2483
|
-
|
2484
|
-
|
2485
|
-
|
1030
|
+
const char *end = str + cnt;
|
1031
|
+
const char *check_start = str;
|
1032
|
+
|
1033
|
+
if (is_sym) {
|
1034
|
+
*out->cur++ = ':';
|
1035
|
+
}
|
1036
|
+
#ifdef HAVE_SIMD_NEON
|
1037
|
+
const char *chunk_start;
|
1038
|
+
const char *chunk_end;
|
1039
|
+
const char *cursor = str;
|
1040
|
+
int neon_state = (cmap_neon != NULL) ? 1 : 4;
|
1041
|
+
char matches[16];
|
1042
|
+
bool do_hi_validation = false;
|
1043
|
+
// uint64_t neon_match_mask = 0;
|
1044
|
+
#define SEARCH_FLUSH \
|
1045
|
+
if (str > cursor) { \
|
1046
|
+
APPEND_CHARS(out->cur, cursor, str - cursor); \
|
1047
|
+
cursor = str; \
|
1048
|
+
}
|
1049
|
+
|
1050
|
+
loop:
|
1051
|
+
#endif /* HAVE_SIMD_NEON */
|
1052
|
+
for (; str < end; str++) {
|
1053
|
+
char action = 0;
|
1054
|
+
#ifdef HAVE_SIMD_NEON
|
1055
|
+
/* neon_state:
|
1056
|
+
* 1: Scanning for matches. There must be at least
|
1057
|
+
sizeof(uint8x16_t) bytes of input data to use SIMD and
|
1058
|
+
cmap_neon must be non-null.
|
1059
|
+
* 2: Matches have been found. Will set str to the position of the
|
1060
|
+
* next match and set the state to 3.
|
1061
|
+
* If there are no more matches it will transition to state 1.
|
1062
|
+
* 4: Fallback to the scalar algorithm. Not enough data to use
|
1063
|
+
* SIMD.
|
1064
|
+
*/
|
1065
|
+
#define NEON_SET_STATE(state) \
|
1066
|
+
neon_state = state; \
|
1067
|
+
goto loop;
|
1068
|
+
#define NEON_RETURN_TO_STATE(state) neon_state = state;
|
1069
|
+
switch (neon_state) {
|
1070
|
+
case 1: {
|
1071
|
+
while (true) {
|
1072
|
+
const char *chunk_ptr = NULL;
|
1073
|
+
if (str + sizeof(uint8x16_t) <= end) {
|
1074
|
+
chunk_ptr = str;
|
1075
|
+
chunk_start = str;
|
1076
|
+
chunk_end = str + sizeof(uint8x16_t);
|
1077
|
+
} else if ((end - str) >= SIMD_MINIMUM_THRESHOLD) {
|
1078
|
+
memset(out->cur, 'A', sizeof(uint8x16_t));
|
1079
|
+
memcpy(out->cur, str, (end - str));
|
1080
|
+
chunk_ptr = out->cur;
|
1081
|
+
chunk_start = str;
|
1082
|
+
chunk_end = end;
|
1083
|
+
} else {
|
1084
|
+
SEARCH_FLUSH;
|
1085
|
+
NEON_SET_STATE(4);
|
1086
|
+
break; /* Unreachable */
|
1087
|
+
}
|
1088
|
+
neon_match_result result = neon_update(chunk_ptr,
|
1089
|
+
cmap_neon,
|
1090
|
+
neon_table_size,
|
1091
|
+
do_unicode_validation,
|
1092
|
+
has_hi);
|
1093
|
+
if ((result.do_unicode_validation) || vmaxvq_u8(result.needs_escape) != 0) {
|
1094
|
+
SEARCH_FLUSH;
|
1095
|
+
uint8x16_t actions = vaddq_u8(result.needs_escape, vdupq_n_u8('1'));
|
1096
|
+
do_hi_validation = result.do_unicode_validation;
|
1097
|
+
vst1q_u8((unsigned char *)matches, actions);
|
1098
|
+
NEON_SET_STATE(2);
|
1099
|
+
break; /* Unreachable */
|
1100
|
+
}
|
1101
|
+
str = chunk_end;
|
1102
|
+
}
|
1103
|
+
// We must have run out of data to use SIMD. Go to state 4.
|
1104
|
+
SEARCH_FLUSH;
|
1105
|
+
NEON_SET_STATE(4);
|
1106
|
+
} break;
|
1107
|
+
case 3:
|
1108
|
+
cursor = str;
|
1109
|
+
// This fall through is intentional. We return to state 3 after we process
|
1110
|
+
// a byte (or multiple). We return to this state to ensure the cursor is
|
1111
|
+
// pointing to the correct location. We then resume looking for matches
|
1112
|
+
// within the previously processed chunk.
|
1113
|
+
case 2:
|
1114
|
+
if (str >= chunk_end) {
|
1115
|
+
NEON_SET_STATE(1);
|
1116
|
+
}
|
1117
|
+
if (!do_hi_validation) {
|
1118
|
+
long i = str - chunk_start;
|
1119
|
+
for (; str < chunk_end; i++) {
|
1120
|
+
if ((action = matches[i]) != '1') {
|
1121
|
+
break;
|
1122
|
+
}
|
1123
|
+
*out->cur++ = *str++;
|
1124
|
+
}
|
1125
|
+
// The loop above may have advanced str and directly output them to out->cur.
|
1126
|
+
// Ensure cursor is set appropriately.
|
1127
|
+
cursor = str;
|
1128
|
+
if (str >= chunk_end) {
|
1129
|
+
// We must have advanced past the end... we are done.
|
1130
|
+
NEON_SET_STATE(1);
|
1131
|
+
}
|
1132
|
+
} else {
|
1133
|
+
long match_index = str - chunk_start;
|
1134
|
+
action = matches[match_index];
|
1135
|
+
}
|
1136
|
+
NEON_RETURN_TO_STATE(3);
|
1137
|
+
break;
|
1138
|
+
case 4: action = cmap[(uint8_t)*str];
|
1139
|
+
}
|
1140
|
+
#undef NEON_SET_STATE
|
1141
|
+
#undef NEON_RETURN_TO_STATE
|
1142
|
+
#else
|
1143
|
+
action = cmap[(uint8_t)*str];
|
1144
|
+
#endif /* HAVE_SIMD_NEON */
|
1145
|
+
switch (action) {
|
1146
|
+
case '1':
|
1147
|
+
if (do_unicode_validation && check_start <= str) {
|
1148
|
+
if (0 != (0x80 & (uint8_t)*str)) {
|
1149
|
+
if (0xC0 == (0xC0 & (uint8_t)*str)) {
|
1150
|
+
check_start = check_unicode(str, end, orig);
|
1151
|
+
} else {
|
1152
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
1153
|
+
}
|
1154
|
+
}
|
1155
|
+
}
|
1156
|
+
*out->cur++ = *str;
|
1157
|
+
break;
|
1158
|
+
case '2':
|
1159
|
+
*out->cur++ = '\\';
|
1160
|
+
switch (*str) {
|
1161
|
+
case '\\': *out->cur++ = '\\'; break;
|
1162
|
+
case '\b': *out->cur++ = 'b'; break;
|
1163
|
+
case '\t': *out->cur++ = 't'; break;
|
1164
|
+
case '\n': *out->cur++ = 'n'; break;
|
1165
|
+
case '\f': *out->cur++ = 'f'; break;
|
1166
|
+
case '\r': *out->cur++ = 'r'; break;
|
1167
|
+
default: *out->cur++ = *str; break;
|
1168
|
+
}
|
1169
|
+
break;
|
1170
|
+
case '3': // Unicode
|
1171
|
+
if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
|
1172
|
+
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
1173
|
+
str = dump_unicode(str, end, out, orig);
|
1174
|
+
} else {
|
1175
|
+
check_start = check_unicode(str, end, orig);
|
1176
|
+
*out->cur++ = *str;
|
1177
|
+
}
|
1178
|
+
break;
|
1179
|
+
}
|
1180
|
+
str = dump_unicode(str, end, out, orig);
|
1181
|
+
break;
|
1182
|
+
case '6': // control characters
|
1183
|
+
if (*(uint8_t *)str < 0x80) {
|
1184
|
+
if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
|
1185
|
+
break;
|
1186
|
+
}
|
1187
|
+
APPEND_CHARS(out->cur, "\\u00", 4);
|
1188
|
+
dump_hex((uint8_t)*str, out);
|
1189
|
+
} else {
|
1190
|
+
if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
|
1191
|
+
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
1192
|
+
str = dump_unicode(str, end, out, orig);
|
1193
|
+
} else {
|
1194
|
+
check_start = check_unicode(str, end, orig);
|
1195
|
+
*out->cur++ = *str;
|
1196
|
+
}
|
1197
|
+
break;
|
1198
|
+
}
|
1199
|
+
str = dump_unicode(str, end, out, orig);
|
1200
|
+
}
|
1201
|
+
break;
|
1202
|
+
default: break; // ignore, should never happen if the table is correct
|
1203
|
+
}
|
1204
|
+
}
|
1205
|
+
*out->cur++ = '"';
|
1206
|
+
}
|
1207
|
+
if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
|
1208
|
+
uint8_t c = (uint8_t)*(str - 1);
|
1209
|
+
int i;
|
1210
|
+
int scnt = (int)(str - orig);
|
1211
|
+
|
1212
|
+
// Last utf-8 characters must be 0x10xxxxxx. The start must be
|
1213
|
+
// 0x110xxxxx for 2 characters, 0x1110xxxx for 3, and 0x11110xxx for
|
1214
|
+
// 4.
|
1215
|
+
if (0 != (0x40 & c)) {
|
1216
|
+
debug_raise(orig, cnt, __LINE__);
|
1217
|
+
}
|
1218
|
+
for (i = 1; i < (int)scnt && i < 4; i++) {
|
1219
|
+
c = str[-1 - i];
|
1220
|
+
if (0x80 != (0xC0 & c)) {
|
1221
|
+
switch (i) {
|
1222
|
+
case 1:
|
1223
|
+
if (0xC0 != (0xE0 & c)) {
|
1224
|
+
debug_raise(orig, cnt, __LINE__);
|
1225
|
+
}
|
1226
|
+
break;
|
1227
|
+
case 2:
|
1228
|
+
if (0xE0 != (0xF0 & c)) {
|
1229
|
+
debug_raise(orig, cnt, __LINE__);
|
1230
|
+
}
|
1231
|
+
break;
|
1232
|
+
case 3:
|
1233
|
+
if (0xF0 != (0xF8 & c)) {
|
1234
|
+
debug_raise(orig, cnt, __LINE__);
|
1235
|
+
}
|
1236
|
+
break;
|
1237
|
+
default: // can't get here
|
1238
|
+
break;
|
1239
|
+
}
|
1240
|
+
break;
|
1241
|
+
}
|
1242
|
+
}
|
1243
|
+
if (i == (int)scnt || 4 <= i) {
|
1244
|
+
debug_raise(orig, cnt, __LINE__);
|
1245
|
+
}
|
2486
1246
|
}
|
2487
1247
|
*out->cur = '\0';
|
2488
1248
|
}
|
2489
1249
|
|
2490
|
-
|
2491
|
-
|
2492
|
-
|
2493
|
-
|
2494
|
-
dump_nil(out);
|
2495
|
-
break;
|
2496
|
-
case T_TRUE:
|
2497
|
-
dump_true(out);
|
2498
|
-
break;
|
2499
|
-
case T_FALSE:
|
2500
|
-
dump_false(out);
|
2501
|
-
break;
|
2502
|
-
case T_STRING:
|
2503
|
-
dump_leaf_str(leaf, out);
|
2504
|
-
break;
|
2505
|
-
case T_FIXNUM:
|
2506
|
-
dump_leaf_fixnum(leaf, out);
|
2507
|
-
break;
|
2508
|
-
case T_FLOAT:
|
2509
|
-
dump_leaf_float(leaf, out);
|
2510
|
-
break;
|
2511
|
-
case T_ARRAY:
|
2512
|
-
dump_leaf_array(leaf, depth, out);
|
2513
|
-
break;
|
2514
|
-
case T_HASH:
|
2515
|
-
dump_leaf_hash(leaf, depth, out);
|
2516
|
-
break;
|
2517
|
-
default:
|
2518
|
-
rb_raise(rb_eTypeError, "Unexpected type %02x.\n", leaf->rtype);
|
2519
|
-
break;
|
2520
|
-
}
|
1250
|
+
void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
|
1251
|
+
const char *s = rb_class2name(obj);
|
1252
|
+
|
1253
|
+
oj_dump_cstr(s, strlen(s), 0, 0, out);
|
2521
1254
|
}
|
2522
1255
|
|
2523
|
-
void
|
2524
|
-
|
2525
|
-
|
2526
|
-
|
2527
|
-
out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
|
2528
|
-
out->allocated = 1;
|
2529
|
-
}
|
2530
|
-
out->cur = out->buf;
|
2531
|
-
out->circ_cnt = 0;
|
2532
|
-
out->opts = copts;
|
2533
|
-
out->hash_cnt = 0;
|
2534
|
-
out->indent = copts->indent;
|
2535
|
-
dump_leaf(leaf, 0, out);
|
1256
|
+
void oj_dump_obj_to_s(VALUE obj, Out out) {
|
1257
|
+
volatile VALUE rstr = oj_safe_string_convert(obj);
|
1258
|
+
|
1259
|
+
oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
|
2536
1260
|
}
|
2537
1261
|
|
2538
|
-
void
|
2539
|
-
|
2540
|
-
|
2541
|
-
|
2542
|
-
|
2543
|
-
FILE *f;
|
1262
|
+
void oj_dump_raw(const char *str, size_t cnt, Out out) {
|
1263
|
+
assure_size(out, cnt + 10);
|
1264
|
+
APPEND_CHARS(out->cur, str, cnt);
|
1265
|
+
*out->cur = '\0';
|
1266
|
+
}
|
2544
1267
|
|
2545
|
-
|
2546
|
-
out
|
2547
|
-
out
|
2548
|
-
out
|
2549
|
-
|
2550
|
-
|
2551
|
-
|
2552
|
-
|
1268
|
+
void oj_out_init(Out out) {
|
1269
|
+
out->buf = out->stack_buffer;
|
1270
|
+
out->cur = out->buf;
|
1271
|
+
out->end = out->buf + sizeof(out->stack_buffer) - BUFFER_EXTRA;
|
1272
|
+
out->allocated = false;
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
void oj_out_free(Out out) {
|
1276
|
+
if (out->allocated) {
|
1277
|
+
OJ_R_FREE(out->buf); // TBD
|
2553
1278
|
}
|
2554
|
-
|
2555
|
-
int err = ferror(f);
|
1279
|
+
}
|
2556
1280
|
|
2557
|
-
|
1281
|
+
void oj_grow_out(Out out, size_t len) {
|
1282
|
+
size_t size = out->end - out->buf;
|
1283
|
+
long pos = out->cur - out->buf;
|
1284
|
+
char *buf = out->buf;
|
1285
|
+
|
1286
|
+
size *= 2;
|
1287
|
+
if (size <= len * 2 + pos) {
|
1288
|
+
size += len;
|
2558
1289
|
}
|
2559
|
-
if (out
|
2560
|
-
|
1290
|
+
if (out->allocated) {
|
1291
|
+
OJ_R_REALLOC_N(buf, char, (size + BUFFER_EXTRA));
|
1292
|
+
} else {
|
1293
|
+
buf = OJ_R_ALLOC_N(char, (size + BUFFER_EXTRA));
|
1294
|
+
out->allocated = true;
|
1295
|
+
memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
|
2561
1296
|
}
|
2562
|
-
|
1297
|
+
if (0 == buf) {
|
1298
|
+
rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]", ENOSPC, strerror(ENOSPC));
|
1299
|
+
}
|
1300
|
+
out->buf = buf;
|
1301
|
+
out->end = buf + size;
|
1302
|
+
out->cur = out->buf + pos;
|
2563
1303
|
}
|
2564
1304
|
|
2565
|
-
|
2566
|
-
|
2567
|
-
|
2568
|
-
|
2569
|
-
|
1305
|
+
void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
|
1306
|
+
assure_size(out, 4);
|
1307
|
+
APPEND_CHARS(out->cur, "null", 4);
|
1308
|
+
*out->cur = '\0';
|
1309
|
+
}
|
2570
1310
|
|
2571
|
-
|
2572
|
-
|
2573
|
-
|
1311
|
+
void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
|
1312
|
+
assure_size(out, 4);
|
1313
|
+
APPEND_CHARS(out->cur, "true", 4);
|
1314
|
+
*out->cur = '\0';
|
2574
1315
|
}
|
2575
1316
|
|
2576
|
-
|
2577
|
-
|
2578
|
-
|
2579
|
-
|
1317
|
+
void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
|
1318
|
+
assure_size(out, 5);
|
1319
|
+
APPEND_CHARS(out->cur, "false", 5);
|
1320
|
+
*out->cur = '\0';
|
1321
|
+
}
|
2580
1322
|
|
2581
|
-
|
2582
|
-
|
1323
|
+
static const char digits_table[] = "\
|
1324
|
+
00010203040506070809\
|
1325
|
+
10111213141516171819\
|
1326
|
+
20212223242526272829\
|
1327
|
+
30313233343536373839\
|
1328
|
+
40414243444546474849\
|
1329
|
+
50515253545556575859\
|
1330
|
+
60616263646566676869\
|
1331
|
+
70717273747576777879\
|
1332
|
+
80818283848586878889\
|
1333
|
+
90919293949596979899";
|
1334
|
+
|
1335
|
+
char *oj_longlong_to_string(long long num, bool negative, char *buf) {
|
1336
|
+
while (100 <= num) {
|
1337
|
+
unsigned idx = num % 100 * 2;
|
1338
|
+
*buf-- = digits_table[idx + 1];
|
1339
|
+
*buf-- = digits_table[idx];
|
1340
|
+
num /= 100;
|
1341
|
+
}
|
1342
|
+
if (num < 10) {
|
1343
|
+
*buf-- = num + '0';
|
1344
|
+
} else {
|
1345
|
+
*buf-- = digits_table[num * 2 + 1];
|
1346
|
+
*buf-- = digits_table[num * 2];
|
2583
1347
|
}
|
2584
|
-
sw->depth++;
|
2585
|
-
sw->types[sw->depth] = type;
|
2586
|
-
}
|
2587
1348
|
|
2588
|
-
|
2589
|
-
|
2590
|
-
|
2591
|
-
|
2592
|
-
sw->types[sw->depth] = ObjectType;
|
2593
|
-
break;
|
2594
|
-
case ArrayNew:
|
2595
|
-
sw->types[sw->depth] = ArrayType;
|
2596
|
-
break;
|
2597
|
-
case ObjectType:
|
2598
|
-
case ArrayType:
|
2599
|
-
// Always have a few characters available in the out.buf.
|
2600
|
-
*sw->out.cur++ = ',';
|
2601
|
-
break;
|
1349
|
+
if (negative) {
|
1350
|
+
*buf = '-';
|
1351
|
+
} else {
|
1352
|
+
buf++;
|
2602
1353
|
}
|
1354
|
+
return buf;
|
2603
1355
|
}
|
2604
1356
|
|
2605
|
-
void
|
2606
|
-
|
2607
|
-
|
2608
|
-
long
|
1357
|
+
void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
1358
|
+
char buf[32];
|
1359
|
+
char *b = buf + sizeof(buf) - 1;
|
1360
|
+
long long num = NUM2LL(obj);
|
1361
|
+
bool neg = false;
|
1362
|
+
size_t cnt = 0;
|
1363
|
+
bool dump_as_string = false;
|
2609
1364
|
|
2610
|
-
if (
|
2611
|
-
|
1365
|
+
if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
|
1366
|
+
(out->opts->int_range_max < num || out->opts->int_range_min > num)) {
|
1367
|
+
dump_as_string = true;
|
2612
1368
|
}
|
2613
|
-
if (
|
2614
|
-
|
1369
|
+
if (0 > num) {
|
1370
|
+
neg = true;
|
1371
|
+
num = -num;
|
2615
1372
|
}
|
2616
|
-
|
2617
|
-
|
2618
|
-
|
1373
|
+
*b-- = '\0';
|
1374
|
+
|
1375
|
+
if (dump_as_string) {
|
1376
|
+
*b-- = '"';
|
2619
1377
|
}
|
2620
|
-
|
2621
|
-
|
2622
|
-
|
1378
|
+
if (0 < num) {
|
1379
|
+
b = oj_longlong_to_string(num, neg, b);
|
1380
|
+
} else {
|
1381
|
+
*b = '0';
|
2623
1382
|
}
|
2624
|
-
|
2625
|
-
|
2626
|
-
|
1383
|
+
if (dump_as_string) {
|
1384
|
+
*--b = '"';
|
1385
|
+
}
|
1386
|
+
cnt = sizeof(buf) - (b - buf) - 1;
|
1387
|
+
assure_size(out, cnt);
|
1388
|
+
APPEND_CHARS(out->cur, b, cnt);
|
1389
|
+
*out->cur = '\0';
|
2627
1390
|
}
|
2628
1391
|
|
2629
|
-
void
|
2630
|
-
|
2631
|
-
|
2632
|
-
|
2633
|
-
if (sw->out.end - sw->out.cur <= 1) {
|
2634
|
-
grow(&sw->out, 1);
|
2635
|
-
}
|
2636
|
-
} else {
|
2637
|
-
long size;
|
2638
|
-
|
2639
|
-
key_check(sw, key);
|
2640
|
-
size = sw->depth * sw->out.indent + 3;
|
2641
|
-
if (sw->out.end - sw->out.cur <= (long)size) {
|
2642
|
-
grow(&sw->out, size);
|
2643
|
-
}
|
2644
|
-
maybe_comma(sw);
|
2645
|
-
if (0 < sw->depth) {
|
2646
|
-
fill_indent(&sw->out, sw->depth);
|
2647
|
-
}
|
2648
|
-
if (0 != key) {
|
2649
|
-
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
2650
|
-
*sw->out.cur++ = ':';
|
2651
|
-
}
|
2652
|
-
}
|
2653
|
-
*sw->out.cur++ = '{';
|
2654
|
-
push_type(sw, ObjectNew);
|
2655
|
-
}
|
1392
|
+
void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
|
1393
|
+
volatile VALUE rs = rb_big2str(obj, 10);
|
1394
|
+
size_t cnt = RSTRING_LEN(rs);
|
1395
|
+
bool dump_as_string = false;
|
2656
1396
|
|
2657
|
-
|
2658
|
-
|
2659
|
-
|
2660
|
-
|
2661
|
-
if (sw->out.end - sw->out.cur <= 1) {
|
2662
|
-
grow(&sw->out, 1);
|
2663
|
-
}
|
1397
|
+
if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
|
1398
|
+
dump_as_string = true;
|
1399
|
+
assure_size(out, cnt + 2);
|
1400
|
+
*out->cur++ = '"';
|
2664
1401
|
} else {
|
2665
|
-
|
2666
|
-
|
2667
|
-
|
2668
|
-
|
2669
|
-
|
2670
|
-
|
2671
|
-
|
2672
|
-
maybe_comma(sw);
|
2673
|
-
if (0 < sw->depth) {
|
2674
|
-
fill_indent(&sw->out, sw->depth);
|
2675
|
-
}
|
2676
|
-
if (0 != key) {
|
2677
|
-
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
2678
|
-
*sw->out.cur++ = ':';
|
2679
|
-
}
|
2680
|
-
}
|
2681
|
-
*sw->out.cur++ = '[';
|
2682
|
-
push_type(sw, ArrayNew);
|
1402
|
+
assure_size(out, cnt);
|
1403
|
+
}
|
1404
|
+
APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
|
1405
|
+
if (dump_as_string) {
|
1406
|
+
*out->cur++ = '"';
|
1407
|
+
}
|
1408
|
+
*out->cur = '\0';
|
2683
1409
|
}
|
2684
1410
|
|
2685
|
-
|
2686
|
-
|
2687
|
-
|
2688
|
-
|
2689
|
-
|
2690
|
-
|
2691
|
-
|
2692
|
-
key_check(sw, key);
|
2693
|
-
size = sw->depth * sw->out.indent + 3;
|
2694
|
-
if (sw->out.end - sw->out.cur <= size) {
|
2695
|
-
grow(&sw->out, size);
|
2696
|
-
}
|
2697
|
-
maybe_comma(sw);
|
2698
|
-
if (0 < sw->depth) {
|
2699
|
-
fill_indent(&sw->out, sw->depth);
|
2700
|
-
}
|
2701
|
-
if (0 != key) {
|
2702
|
-
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
2703
|
-
*sw->out.cur++ = ':';
|
2704
|
-
}
|
2705
|
-
}
|
2706
|
-
dump_val(val, sw->depth, &sw->out, 0, 0, true);
|
2707
|
-
}
|
1411
|
+
// Removed dependencies on math due to problems with CentOS 5.4.
|
1412
|
+
void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
1413
|
+
char buf[64];
|
1414
|
+
char *b;
|
1415
|
+
double d = rb_num2dbl(obj);
|
1416
|
+
size_t cnt = 0;
|
2708
1417
|
|
2709
|
-
|
2710
|
-
|
2711
|
-
|
2712
|
-
|
1418
|
+
if (0.0 == d) {
|
1419
|
+
b = buf;
|
1420
|
+
*b++ = '0';
|
1421
|
+
*b++ = '.';
|
1422
|
+
*b++ = '0';
|
1423
|
+
*b++ = '\0';
|
1424
|
+
cnt = 3;
|
1425
|
+
} else if (OJ_INFINITY == d) {
|
1426
|
+
if (ObjectMode == out->opts->mode) {
|
1427
|
+
strcpy(buf, inf_val);
|
1428
|
+
cnt = sizeof(inf_val) - 1;
|
1429
|
+
} else {
|
1430
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
1431
|
+
|
1432
|
+
if (AutoNan == nd) {
|
1433
|
+
switch (out->opts->mode) {
|
1434
|
+
case CompatMode: nd = WordNan; break;
|
1435
|
+
case StrictMode: nd = RaiseNan; break;
|
1436
|
+
case NullMode: nd = NullNan; break;
|
1437
|
+
case CustomMode: nd = NullNan; break;
|
1438
|
+
default: break;
|
1439
|
+
}
|
1440
|
+
}
|
1441
|
+
switch (nd) {
|
1442
|
+
case RaiseNan: raise_strict(obj); break;
|
1443
|
+
case WordNan:
|
1444
|
+
strcpy(buf, "Infinity");
|
1445
|
+
cnt = 8;
|
1446
|
+
break;
|
1447
|
+
case NullNan:
|
1448
|
+
strcpy(buf, "null");
|
1449
|
+
cnt = 4;
|
1450
|
+
break;
|
1451
|
+
case HugeNan:
|
1452
|
+
default:
|
1453
|
+
strcpy(buf, inf_val);
|
1454
|
+
cnt = sizeof(inf_val) - 1;
|
1455
|
+
break;
|
1456
|
+
}
|
1457
|
+
}
|
1458
|
+
} else if (-OJ_INFINITY == d) {
|
1459
|
+
if (ObjectMode == out->opts->mode) {
|
1460
|
+
strcpy(buf, ninf_val);
|
1461
|
+
cnt = sizeof(ninf_val) - 1;
|
1462
|
+
} else {
|
1463
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
1464
|
+
|
1465
|
+
if (AutoNan == nd) {
|
1466
|
+
switch (out->opts->mode) {
|
1467
|
+
case CompatMode: nd = WordNan; break;
|
1468
|
+
case StrictMode: nd = RaiseNan; break;
|
1469
|
+
case NullMode: nd = NullNan; break;
|
1470
|
+
default: break;
|
1471
|
+
}
|
1472
|
+
}
|
1473
|
+
switch (nd) {
|
1474
|
+
case RaiseNan: raise_strict(obj); break;
|
1475
|
+
case WordNan:
|
1476
|
+
strcpy(buf, "-Infinity");
|
1477
|
+
cnt = 9;
|
1478
|
+
break;
|
1479
|
+
case NullNan:
|
1480
|
+
strcpy(buf, "null");
|
1481
|
+
cnt = 4;
|
1482
|
+
break;
|
1483
|
+
case HugeNan:
|
1484
|
+
default:
|
1485
|
+
strcpy(buf, ninf_val);
|
1486
|
+
cnt = sizeof(ninf_val) - 1;
|
1487
|
+
break;
|
1488
|
+
}
|
1489
|
+
}
|
1490
|
+
} else if (isnan(d)) {
|
1491
|
+
if (ObjectMode == out->opts->mode) {
|
1492
|
+
strcpy(buf, nan_val);
|
1493
|
+
cnt = sizeof(nan_val) - 1;
|
1494
|
+
} else {
|
1495
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
1496
|
+
|
1497
|
+
if (AutoNan == nd) {
|
1498
|
+
switch (out->opts->mode) {
|
1499
|
+
case ObjectMode: nd = HugeNan; break;
|
1500
|
+
case StrictMode: nd = RaiseNan; break;
|
1501
|
+
case NullMode: nd = NullNan; break;
|
1502
|
+
default: break;
|
1503
|
+
}
|
1504
|
+
}
|
1505
|
+
switch (nd) {
|
1506
|
+
case RaiseNan: raise_strict(obj); break;
|
1507
|
+
case WordNan:
|
1508
|
+
strcpy(buf, "NaN");
|
1509
|
+
cnt = 3;
|
1510
|
+
break;
|
1511
|
+
case NullNan:
|
1512
|
+
strcpy(buf, "null");
|
1513
|
+
cnt = 4;
|
1514
|
+
break;
|
1515
|
+
case HugeNan:
|
1516
|
+
default:
|
1517
|
+
strcpy(buf, nan_val);
|
1518
|
+
cnt = sizeof(nan_val) - 1;
|
1519
|
+
break;
|
1520
|
+
}
|
1521
|
+
}
|
1522
|
+
} else if (d == (double)(long long int)d) {
|
1523
|
+
cnt = snprintf(buf, sizeof(buf), "%.1f", d);
|
1524
|
+
} else if (0 == out->opts->float_prec) {
|
1525
|
+
volatile VALUE rstr = oj_safe_string_convert(obj);
|
1526
|
+
|
1527
|
+
cnt = RSTRING_LEN(rstr);
|
1528
|
+
if ((int)sizeof(buf) <= cnt) {
|
1529
|
+
cnt = sizeof(buf) - 1;
|
1530
|
+
}
|
1531
|
+
memcpy(buf, RSTRING_PTR(rstr), cnt);
|
1532
|
+
buf[cnt] = '\0';
|
2713
1533
|
} else {
|
2714
|
-
|
2715
|
-
|
2716
|
-
key_check(sw, key);
|
2717
|
-
size = sw->depth * sw->out.indent + 3;
|
2718
|
-
if (sw->out.end - sw->out.cur <= size) {
|
2719
|
-
grow(&sw->out, size);
|
2720
|
-
}
|
2721
|
-
maybe_comma(sw);
|
2722
|
-
if (0 < sw->depth) {
|
2723
|
-
fill_indent(&sw->out, sw->depth);
|
2724
|
-
}
|
2725
|
-
if (0 != key) {
|
2726
|
-
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
2727
|
-
*sw->out.cur++ = ':';
|
2728
|
-
}
|
2729
|
-
}
|
2730
|
-
dump_raw(json, strlen(json), &sw->out);
|
2731
|
-
}
|
2732
|
-
|
2733
|
-
void
|
2734
|
-
oj_str_writer_pop(StrWriter sw) {
|
2735
|
-
long size;
|
2736
|
-
DumpType type = sw->types[sw->depth];
|
2737
|
-
|
2738
|
-
if (sw->keyWritten) {
|
2739
|
-
sw->keyWritten = 0;
|
2740
|
-
rb_raise(rb_eStandardError, "Can not pop after writing a key but no value.");
|
2741
|
-
}
|
2742
|
-
sw->depth--;
|
2743
|
-
if (0 > sw->depth) {
|
2744
|
-
rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
|
2745
|
-
}
|
2746
|
-
size = sw->depth * sw->out.indent + 2;
|
2747
|
-
if (sw->out.end - sw->out.cur <= size) {
|
2748
|
-
grow(&sw->out, size);
|
2749
|
-
}
|
2750
|
-
fill_indent(&sw->out, sw->depth);
|
2751
|
-
switch (type) {
|
2752
|
-
case ObjectNew:
|
2753
|
-
case ObjectType:
|
2754
|
-
*sw->out.cur++ = '}';
|
2755
|
-
break;
|
2756
|
-
case ArrayNew:
|
2757
|
-
case ArrayType:
|
2758
|
-
*sw->out.cur++ = ']';
|
2759
|
-
break;
|
2760
|
-
}
|
2761
|
-
if (0 == sw->depth && 0 <= sw->out.indent) {
|
2762
|
-
*sw->out.cur++ = '\n';
|
1534
|
+
cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
|
2763
1535
|
}
|
1536
|
+
assure_size(out, cnt);
|
1537
|
+
APPEND_CHARS(out->cur, buf, cnt);
|
1538
|
+
*out->cur = '\0';
|
2764
1539
|
}
|
2765
1540
|
|
2766
|
-
|
2767
|
-
|
2768
|
-
|
2769
|
-
|
1541
|
+
size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
|
1542
|
+
size_t cnt = snprintf(buf, blen, format, d);
|
1543
|
+
|
1544
|
+
// Round off issues at 16 significant digits so check for obvious ones of
|
1545
|
+
// 0001 and 9999.
|
1546
|
+
if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
|
1547
|
+
volatile VALUE rstr = oj_safe_string_convert(obj);
|
1548
|
+
|
1549
|
+
strcpy(buf, RSTRING_PTR(rstr));
|
1550
|
+
cnt = RSTRING_LEN(rstr);
|
2770
1551
|
}
|
1552
|
+
return cnt;
|
2771
1553
|
}
|