oj 2.18.3 → 3.13.14
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 +1324 -0
- data/README.md +51 -204
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +49 -72
- data/ext/oj/cache.c +326 -0
- data/ext/oj/cache.h +21 -0
- data/ext/oj/cache8.c +61 -64
- data/ext/oj/cache8.h +12 -39
- data/ext/oj/circarray.c +37 -68
- data/ext/oj/circarray.h +16 -42
- data/ext/oj/code.c +221 -0
- data/ext/oj/code.h +40 -0
- data/ext/oj/compat.c +231 -107
- data/ext/oj/custom.c +1125 -0
- data/ext/oj/debug.c +132 -0
- data/ext/oj/dump.c +935 -2513
- data/ext/oj/dump.h +108 -0
- data/ext/oj/dump_compat.c +936 -0
- data/ext/oj/dump_leaf.c +164 -0
- data/ext/oj/dump_object.c +761 -0
- data/ext/oj/dump_strict.c +410 -0
- data/ext/oj/encode.h +7 -42
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +40 -54
- data/ext/oj/err.h +52 -46
- data/ext/oj/extconf.rb +21 -30
- data/ext/oj/fast.c +1097 -1080
- data/ext/oj/intern.c +301 -0
- data/ext/oj/intern.h +26 -0
- data/ext/oj/mimic_json.c +893 -0
- data/ext/oj/object.c +549 -620
- data/ext/oj/odd.c +155 -167
- data/ext/oj/odd.h +37 -63
- data/ext/oj/oj.c +1661 -2063
- data/ext/oj/oj.h +341 -270
- data/ext/oj/parse.c +974 -737
- data/ext/oj/parse.h +105 -97
- data/ext/oj/parser.c +1526 -0
- data/ext/oj/parser.h +90 -0
- data/ext/oj/rails.c +1504 -0
- data/ext/oj/rails.h +18 -0
- data/ext/oj/reader.c +141 -163
- data/ext/oj/reader.h +75 -113
- data/ext/oj/resolve.c +45 -93
- data/ext/oj/resolve.h +7 -34
- data/ext/oj/rxclass.c +143 -0
- data/ext/oj/rxclass.h +26 -0
- data/ext/oj/saj.c +447 -511
- data/ext/oj/saj2.c +348 -0
- data/ext/oj/scp.c +91 -138
- data/ext/oj/sparse.c +793 -644
- data/ext/oj/stream_writer.c +331 -0
- data/ext/oj/strict.c +145 -109
- data/ext/oj/string_writer.c +493 -0
- data/ext/oj/trace.c +72 -0
- data/ext/oj/trace.h +28 -0
- data/ext/oj/usual.c +1254 -0
- data/ext/oj/util.c +136 -0
- data/ext/oj/util.h +20 -0
- data/ext/oj/val_stack.c +62 -70
- data/ext/oj/val_stack.h +95 -129
- data/ext/oj/validate.c +51 -0
- data/ext/oj/wab.c +622 -0
- data/lib/oj/bag.rb +1 -0
- data/lib/oj/easy_hash.rb +17 -8
- data/lib/oj/error.rb +10 -11
- data/lib/oj/json.rb +176 -0
- data/lib/oj/mimic.rb +158 -19
- data/lib/oj/state.rb +132 -0
- data/lib/oj/version.rb +2 -2
- data/lib/oj.rb +1 -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/JsonGem.md +94 -0
- data/pages/Modes.md +161 -0
- data/pages/Options.md +327 -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
- data/test/activerecord/result_test.rb +32 -0
- data/test/activesupport4/decoding_test.rb +108 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/abstract_unit.rb +45 -0
- data/test/activesupport5/decoding_test.rb +133 -0
- data/test/activesupport5/encoding_test.rb +500 -0
- data/test/activesupport5/encoding_test_cases.rb +98 -0
- data/test/activesupport5/test_helper.rb +72 -0
- data/test/activesupport5/time_zone_test_helpers.rb +39 -0
- data/test/activesupport6/abstract_unit.rb +44 -0
- data/test/activesupport6/decoding_test.rb +133 -0
- data/test/activesupport6/encoding_test.rb +507 -0
- data/test/activesupport6/encoding_test_cases.rb +98 -0
- data/test/activesupport6/test_common.rb +17 -0
- data/test/activesupport6/test_helper.rb +163 -0
- data/test/activesupport6/time_zone_test_helpers.rb +39 -0
- data/test/activesupport7/abstract_unit.rb +49 -0
- data/test/activesupport7/decoding_test.rb +125 -0
- data/test/activesupport7/encoding_test.rb +486 -0
- data/test/activesupport7/encoding_test_cases.rb +104 -0
- data/test/activesupport7/time_zone_test_helpers.rb +47 -0
- data/test/bar.rb +9 -0
- data/test/baz.rb +16 -0
- data/test/bug.rb +11 -46
- data/test/foo.rb +69 -16
- data/test/helper.rb +10 -1
- data/test/isolated/shared.rb +12 -8
- data/test/isolated/test_mimic_rails_after.rb +3 -3
- data/test/isolated/test_mimic_rails_before.rb +3 -3
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +153 -0
- data/test/json_gem/json_encoding_test.rb +107 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +397 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +26 -0
- data/test/mem.rb +33 -0
- data/test/perf.rb +1 -1
- data/test/perf_compat.rb +30 -28
- data/test/perf_dump.rb +50 -0
- data/test/perf_object.rb +1 -1
- data/test/perf_once.rb +58 -0
- data/test/perf_parser.rb +189 -0
- data/test/perf_scp.rb +11 -10
- data/test/perf_strict.rb +30 -19
- data/test/perf_wab.rb +131 -0
- data/test/prec.rb +23 -0
- data/test/sample.rb +0 -1
- data/test/sample_json.rb +1 -1
- data/test/test_compat.rb +219 -102
- data/test/test_custom.rb +533 -0
- data/test/test_fast.rb +107 -35
- data/test/test_file.rb +19 -25
- data/test/test_generate.rb +21 -0
- data/test/test_hash.rb +11 -1
- data/test/test_integer_range.rb +72 -0
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +357 -70
- data/test/test_parser.rb +27 -0
- data/test/test_parser_saj.rb +245 -0
- data/test/test_parser_usual.rb +217 -0
- data/test/test_rails.rb +35 -0
- data/test/test_saj.rb +1 -1
- data/test/test_scp.rb +39 -2
- data/test/test_strict.rb +186 -7
- data/test/test_various.rb +160 -774
- data/test/test_wab.rb +307 -0
- data/test/test_writer.rb +90 -2
- data/test/tests.rb +24 -0
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- data/test/zoo.rb +13 -0
- metadata +194 -56
- data/ext/oj/hash.c +0 -163
- data/ext/oj/hash.h +0 -46
- data/ext/oj/hash_test.c +0 -512
- data/test/activesupport_datetime_test.rb +0 -23
- 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/io.rb +0 -48
- data/test/isolated/test_mimic_rails_datetime.rb +0 -27
- data/test/mod.rb +0 -16
- data/test/rails.rb +0 -50
- data/test/russian.rb +0 -18
- data/test/struct.rb +0 -29
- data/test/test_serializer.rb +0 -59
- data/test/write_timebars.rb +0 -31
data/ext/oj/dump.c
CHANGED
|
@@ -1,118 +1,44 @@
|
|
|
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"
|
|
45
18
|
#include "odd.h"
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#endif
|
|
19
|
+
#include "oj.h"
|
|
20
|
+
#include "trace.h"
|
|
21
|
+
#include "util.h"
|
|
50
22
|
|
|
51
23
|
// 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
|
|
24
|
+
#define OJ_INFINITY (1.0 / 0.0)
|
|
56
25
|
|
|
57
26
|
#define MAX_DEPTH 1000
|
|
58
27
|
|
|
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);
|
|
28
|
+
static const char inf_val[] = INF_VAL;
|
|
29
|
+
static const char ninf_val[] = NINF_VAL;
|
|
30
|
+
static const char nan_val[] = NAN_VAL;
|
|
99
31
|
|
|
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);
|
|
32
|
+
typedef unsigned long ulong;
|
|
104
33
|
|
|
105
|
-
static
|
|
106
|
-
static
|
|
107
|
-
static
|
|
108
|
-
static void dump_leaf_float(Leaf leaf, Out out);
|
|
109
|
-
static void dump_leaf_array(Leaf leaf, int depth, Out out);
|
|
110
|
-
static void dump_leaf_hash(Leaf leaf, int depth, Out out);
|
|
34
|
+
static size_t hibit_friendly_size(const uint8_t *str, size_t len);
|
|
35
|
+
static size_t xss_friendly_size(const uint8_t *str, size_t len);
|
|
36
|
+
static size_t ascii_friendly_size(const uint8_t *str, size_t len);
|
|
111
37
|
|
|
112
|
-
static const char
|
|
38
|
+
static const char hex_chars[17] = "0123456789abcdef";
|
|
113
39
|
|
|
114
40
|
// JSON standard except newlines are no escaped
|
|
115
|
-
static char
|
|
41
|
+
static char newline_friendly_chars[256] = "\
|
|
116
42
|
66666666221622666666666666666666\
|
|
117
43
|
11211111111111111111111111111111\
|
|
118
44
|
11111111111111111111111111112111\
|
|
@@ -123,7 +49,7 @@ static char newline_friendly_chars[256] = "\
|
|
|
123
49
|
11111111111111111111111111111111";
|
|
124
50
|
|
|
125
51
|
// JSON standard
|
|
126
|
-
static char
|
|
52
|
+
static char hibit_friendly_chars[256] = "\
|
|
127
53
|
66666666222622666666666666666666\
|
|
128
54
|
11211111111111111111111111111111\
|
|
129
55
|
11111111111111111111111111112111\
|
|
@@ -135,7 +61,7 @@ static char hibit_friendly_chars[256] = "\
|
|
|
135
61
|
|
|
136
62
|
// High bit set characters are always encoded as unicode. Worse case is 3
|
|
137
63
|
// bytes per character in the output. That makes this conservative.
|
|
138
|
-
static char
|
|
64
|
+
static char ascii_friendly_chars[256] = "\
|
|
139
65
|
66666666222622666666666666666666\
|
|
140
66
|
11211111111111111111111111111111\
|
|
141
67
|
11111111111111111111111111112111\
|
|
@@ -146,7 +72,7 @@ static char ascii_friendly_chars[256] = "\
|
|
|
146
72
|
33333333333333333333333333333333";
|
|
147
73
|
|
|
148
74
|
// XSS safe mode
|
|
149
|
-
static char
|
|
75
|
+
static char xss_friendly_chars[256] = "\
|
|
150
76
|
66666666222622666666666666666666\
|
|
151
77
|
11211161111111121111111111116161\
|
|
152
78
|
11111111111111111111111111112111\
|
|
@@ -156,2616 +82,1112 @@ static char xss_friendly_chars[256] = "\
|
|
|
156
82
|
33333333333333333333333333333333\
|
|
157
83
|
33333333333333333333333333333333";
|
|
158
84
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
85
|
+
// JSON XSS combo
|
|
86
|
+
static char hixss_friendly_chars[256] = "\
|
|
87
|
+
66666666222622666666666666666666\
|
|
88
|
+
11211111111111111111111111111111\
|
|
89
|
+
11111111111111111111111111112111\
|
|
90
|
+
11111111111111111111111111111111\
|
|
91
|
+
11111111111111111111111111111111\
|
|
92
|
+
11111111111111111111111111111111\
|
|
93
|
+
11111111111111111111111111111111\
|
|
94
|
+
11611111111111111111111111111111";
|
|
162
95
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
96
|
+
// Rails XSS combo
|
|
97
|
+
static char rails_xss_friendly_chars[256] = "\
|
|
98
|
+
66666666222622666666666666666666\
|
|
99
|
+
11211161111111111111111111116161\
|
|
100
|
+
11111111111111111111111111112111\
|
|
101
|
+
11111111111111111111111111111111\
|
|
102
|
+
11111111111111111111111111111111\
|
|
103
|
+
11111111111111111111111111111111\
|
|
104
|
+
11111111111111111111111111111111\
|
|
105
|
+
11611111111111111111111111111111";
|
|
168
106
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
107
|
+
// Rails HTML non-escape
|
|
108
|
+
static char rails_friendly_chars[256] = "\
|
|
109
|
+
66666666222622666666666666666666\
|
|
110
|
+
11211111111111111111111111111111\
|
|
111
|
+
11111111111111111111111111112111\
|
|
112
|
+
11111111111111111111111111111111\
|
|
113
|
+
11111111111111111111111111111111\
|
|
114
|
+
11111111111111111111111111111111\
|
|
115
|
+
11111111111111111111111111111111\
|
|
116
|
+
11111111111111111111111111111111";
|
|
172
117
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
return size - len * (size_t)'0';
|
|
118
|
+
static void raise_strict(VALUE obj) {
|
|
119
|
+
rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.", rb_class2name(rb_obj_class(obj)));
|
|
177
120
|
}
|
|
178
121
|
|
|
179
|
-
inline static size_t
|
|
180
|
-
|
|
181
|
-
size_t
|
|
122
|
+
inline static size_t calculate_string_size(const uint8_t *str, size_t len, const char *table) {
|
|
123
|
+
size_t size = 0;
|
|
124
|
+
size_t i = len;
|
|
182
125
|
|
|
183
|
-
for (;
|
|
184
|
-
|
|
126
|
+
for (; 3 < i; i -= 4) {
|
|
127
|
+
size += table[*str++];
|
|
128
|
+
size += table[*str++];
|
|
129
|
+
size += table[*str++];
|
|
130
|
+
size += table[*str++];
|
|
185
131
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
inline static size_t
|
|
190
|
-
xss_friendly_size(const uint8_t *str, size_t len) {
|
|
191
|
-
size_t size = 0;
|
|
192
|
-
|
|
193
|
-
for (; 0 < len; str++, len--) {
|
|
194
|
-
size += xss_friendly_chars[*str];
|
|
132
|
+
for (; 0 < i; i--) {
|
|
133
|
+
size += table[*str++];
|
|
195
134
|
}
|
|
196
135
|
return size - len * (size_t)'0';
|
|
197
136
|
}
|
|
198
137
|
|
|
199
|
-
inline static
|
|
200
|
-
|
|
201
|
-
if (0 < out->indent) {
|
|
202
|
-
cnt *= out->indent;
|
|
203
|
-
*out->cur++ = '\n';
|
|
204
|
-
for (; 0 < cnt; cnt--) {
|
|
205
|
-
*out->cur++ = ' ';
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
inline static void
|
|
211
|
-
dump_ulong(unsigned long num, Out out) {
|
|
212
|
-
char buf[32];
|
|
213
|
-
char *b = buf + sizeof(buf) - 1;
|
|
214
|
-
|
|
215
|
-
*b-- = '\0';
|
|
216
|
-
if (0 < num) {
|
|
217
|
-
for (; 0 < num; num /= 10, b--) {
|
|
218
|
-
*b = (num % 10) + '0';
|
|
219
|
-
}
|
|
220
|
-
b++;
|
|
221
|
-
} else {
|
|
222
|
-
*b = '0';
|
|
223
|
-
}
|
|
224
|
-
for (; '\0' != *b; b++) {
|
|
225
|
-
*out->cur++ = *b;
|
|
226
|
-
}
|
|
227
|
-
*out->cur = '\0';
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
static void
|
|
231
|
-
grow(Out out, size_t len) {
|
|
232
|
-
size_t size = out->end - out->buf;
|
|
233
|
-
long pos = out->cur - out->buf;
|
|
234
|
-
char *buf;
|
|
235
|
-
|
|
236
|
-
size *= 2;
|
|
237
|
-
if (size <= len * 2 + pos) {
|
|
238
|
-
size += len;
|
|
239
|
-
}
|
|
240
|
-
if (out->allocated) {
|
|
241
|
-
buf = REALLOC_N(out->buf, char, (size + BUFFER_EXTRA));
|
|
242
|
-
} else {
|
|
243
|
-
buf = ALLOC_N(char, (size + BUFFER_EXTRA));
|
|
244
|
-
out->allocated = 1;
|
|
245
|
-
memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
|
|
246
|
-
}
|
|
247
|
-
if (0 == buf) {
|
|
248
|
-
rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]\n", ENOSPC, strerror(ENOSPC));
|
|
249
|
-
}
|
|
250
|
-
out->buf = buf;
|
|
251
|
-
out->end = buf + size;
|
|
252
|
-
out->cur = out->buf + pos;
|
|
138
|
+
inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
|
|
139
|
+
return calculate_string_size(str, len, newline_friendly_chars);
|
|
253
140
|
}
|
|
254
141
|
|
|
255
|
-
inline static
|
|
256
|
-
|
|
257
|
-
uint8_t d = (c >> 4) & 0x0F;
|
|
258
|
-
|
|
259
|
-
*out->cur++ = hex_chars[d];
|
|
260
|
-
d = c & 0x0F;
|
|
261
|
-
*out->cur++ = hex_chars[d];
|
|
142
|
+
inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
|
|
143
|
+
return calculate_string_size(str, len, hibit_friendly_chars);
|
|
262
144
|
}
|
|
263
145
|
|
|
264
|
-
static
|
|
265
|
-
|
|
266
|
-
if (out->end - out->cur <= (long)cnt + 10) {
|
|
267
|
-
grow(out, cnt + 10);
|
|
268
|
-
}
|
|
269
|
-
memcpy(out->cur, str, cnt);
|
|
270
|
-
out->cur += cnt;
|
|
271
|
-
*out->cur = '\0';
|
|
146
|
+
inline static size_t ascii_friendly_size(const uint8_t *str, size_t len) {
|
|
147
|
+
return calculate_string_size(str, len, ascii_friendly_chars);
|
|
272
148
|
}
|
|
273
149
|
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
uint32_t code = 0;
|
|
277
|
-
uint8_t b = *(uint8_t*)str;
|
|
278
|
-
int i, cnt;
|
|
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;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// returns 0 if not using circular references, -1 if not further writing is
|
|
328
|
-
// needed (duplicate), and a positive value if the object was added to the cache.
|
|
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;
|
|
150
|
+
inline static size_t xss_friendly_size(const uint8_t *str, size_t len) {
|
|
151
|
+
return calculate_string_size(str, len, xss_friendly_chars);
|
|
353
152
|
}
|
|
354
153
|
|
|
355
|
-
static
|
|
356
|
-
|
|
357
|
-
size_t
|
|
154
|
+
inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
|
|
155
|
+
size_t size = 0;
|
|
156
|
+
size_t i = len;
|
|
157
|
+
bool check = false;
|
|
358
158
|
|
|
359
|
-
|
|
360
|
-
|
|
159
|
+
for (; 0 < i; str++, i--) {
|
|
160
|
+
size += hixss_friendly_chars[*str];
|
|
161
|
+
if (0 != (0x80 & *str)) {
|
|
162
|
+
check = true;
|
|
163
|
+
}
|
|
361
164
|
}
|
|
362
|
-
*
|
|
363
|
-
*out->cur++ = 'u';
|
|
364
|
-
*out->cur++ = 'l';
|
|
365
|
-
*out->cur++ = 'l';
|
|
366
|
-
*out->cur = '\0';
|
|
165
|
+
return size - len * (size_t)'0' + check;
|
|
367
166
|
}
|
|
368
167
|
|
|
369
|
-
static
|
|
370
|
-
|
|
371
|
-
size_t
|
|
168
|
+
inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
|
|
169
|
+
long size = 0;
|
|
170
|
+
size_t i = len;
|
|
171
|
+
uint8_t hi = 0;
|
|
372
172
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
*out->cur++ = 't';
|
|
377
|
-
*out->cur++ = 'r';
|
|
378
|
-
*out->cur++ = 'u';
|
|
379
|
-
*out->cur++ = 'e';
|
|
380
|
-
*out->cur = '\0';
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
static void
|
|
384
|
-
dump_false(Out out) {
|
|
385
|
-
size_t size = 5;
|
|
386
|
-
|
|
387
|
-
if (out->end - out->cur <= (long)size) {
|
|
388
|
-
grow(out, size);
|
|
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
|
-
}
|
|
397
|
-
|
|
398
|
-
static void
|
|
399
|
-
dump_fixnum(VALUE obj, Out out) {
|
|
400
|
-
char buf[32];
|
|
401
|
-
char *b = buf + sizeof(buf) - 1;
|
|
402
|
-
long long num = rb_num2ll(obj);
|
|
403
|
-
int neg = 0;
|
|
404
|
-
|
|
405
|
-
if (0 > num) {
|
|
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));
|
|
173
|
+
for (; 0 < i; str++, i--) {
|
|
174
|
+
size += rails_xss_friendly_chars[*str];
|
|
175
|
+
hi |= *str & 0x80;
|
|
424
176
|
}
|
|
425
|
-
|
|
426
|
-
|
|
177
|
+
if (0 == hi) {
|
|
178
|
+
return size - len * (size_t)'0';
|
|
427
179
|
}
|
|
428
|
-
*
|
|
180
|
+
return -(size - len * (size_t)'0');
|
|
429
181
|
}
|
|
430
182
|
|
|
431
|
-
static
|
|
432
|
-
|
|
433
|
-
volatile VALUE rs = rb_big2str(obj, 10);
|
|
434
|
-
int cnt = (int)RSTRING_LEN(rs);
|
|
435
|
-
|
|
436
|
-
if (out->end - out->cur <= (long)cnt) {
|
|
437
|
-
grow(out, cnt);
|
|
438
|
-
}
|
|
439
|
-
memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
|
|
440
|
-
out->cur += cnt;
|
|
441
|
-
*out->cur = '\0';
|
|
183
|
+
inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
|
|
184
|
+
return calculate_string_size(str, len, rails_friendly_chars);
|
|
442
185
|
}
|
|
443
186
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
static const char nan_val[] = NAN_VAL;
|
|
447
|
-
|
|
448
|
-
// Removed dependencies on math due to problems with CentOS 5.4.
|
|
449
|
-
static void
|
|
450
|
-
dump_float(VALUE obj, Out out) {
|
|
451
|
-
char buf[64];
|
|
452
|
-
char *b;
|
|
453
|
-
double d = rb_num2dbl(obj);
|
|
454
|
-
int cnt = 0;
|
|
187
|
+
const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
|
|
188
|
+
const char *str = NULL;
|
|
455
189
|
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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 = 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);
|
|
578
|
-
}
|
|
579
|
-
if (out->end - out->cur <= (long)cnt) {
|
|
580
|
-
grow(out, cnt);
|
|
190
|
+
if (AutoNan == opt) {
|
|
191
|
+
switch (mode) {
|
|
192
|
+
case CompatMode: opt = WordNan; break;
|
|
193
|
+
case StrictMode: opt = RaiseNan; break;
|
|
194
|
+
default: break;
|
|
195
|
+
}
|
|
581
196
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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:
|
|
197
|
+
switch (opt) {
|
|
198
|
+
case RaiseNan: raise_strict(obj); break;
|
|
199
|
+
case WordNan:
|
|
200
|
+
if (plus) {
|
|
201
|
+
str = "Infinity";
|
|
202
|
+
*lenp = 8;
|
|
203
|
+
} else {
|
|
204
|
+
str = "-Infinity";
|
|
205
|
+
*lenp = 9;
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
case NullNan:
|
|
209
|
+
str = "null";
|
|
210
|
+
*lenp = 4;
|
|
211
|
+
break;
|
|
212
|
+
case HugeNan:
|
|
607
213
|
default:
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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
|
-
}
|
|
675
|
-
|
|
676
|
-
static void
|
|
677
|
-
dump_str_comp(VALUE obj, Out out) {
|
|
678
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
static void
|
|
682
|
-
dump_str_obj(VALUE obj, VALUE clas, int depth, Out out) {
|
|
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 = RSTRING_LEN(obj);
|
|
688
|
-
char s1 = s[1];
|
|
689
|
-
|
|
690
|
-
dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
|
|
214
|
+
if (plus) {
|
|
215
|
+
str = inf_val;
|
|
216
|
+
*lenp = sizeof(inf_val) - 1;
|
|
217
|
+
} else {
|
|
218
|
+
str = ninf_val;
|
|
219
|
+
*lenp = sizeof(ninf_val) - 1;
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
691
222
|
}
|
|
223
|
+
return str;
|
|
692
224
|
}
|
|
693
225
|
|
|
694
|
-
static void
|
|
695
|
-
|
|
696
|
-
const char *sym = rb_id2name(SYM2ID(obj));
|
|
697
|
-
|
|
698
|
-
dump_cstr(sym, strlen(sym), 0, 0, out);
|
|
699
|
-
}
|
|
226
|
+
inline static void dump_hex(uint8_t c, Out out) {
|
|
227
|
+
uint8_t d = (c >> 4) & 0x0F;
|
|
700
228
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
size_t len = strlen(sym);
|
|
705
|
-
|
|
706
|
-
dump_cstr(sym, len, 1, 0, out);
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
static void
|
|
710
|
-
dump_class_comp(VALUE obj, Out out) {
|
|
711
|
-
const char *s = rb_class2name(obj);
|
|
712
|
-
|
|
713
|
-
dump_cstr(s, strlen(s), 0, 0, out);
|
|
229
|
+
*out->cur++ = hex_chars[d];
|
|
230
|
+
d = c & 0x0F;
|
|
231
|
+
*out->cur++ = hex_chars[d];
|
|
714
232
|
}
|
|
715
233
|
|
|
716
|
-
static void
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
234
|
+
static void raise_invalid_unicode(const char *str, int len, int pos) {
|
|
235
|
+
char c;
|
|
236
|
+
char code[32];
|
|
237
|
+
char *cp = code;
|
|
238
|
+
int i;
|
|
239
|
+
uint8_t d;
|
|
720
240
|
|
|
721
|
-
|
|
722
|
-
|
|
241
|
+
*cp++ = '[';
|
|
242
|
+
for (i = pos; i < len && i - pos < 5; i++) {
|
|
243
|
+
c = str[i];
|
|
244
|
+
d = (c >> 4) & 0x0F;
|
|
245
|
+
*cp++ = hex_chars[d];
|
|
246
|
+
d = c & 0x0F;
|
|
247
|
+
*cp++ = hex_chars[d];
|
|
248
|
+
*cp++ = ' ';
|
|
723
249
|
}
|
|
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';
|
|
250
|
+
cp--;
|
|
251
|
+
*cp++ = ']';
|
|
252
|
+
*cp = '\0';
|
|
253
|
+
rb_raise(oj_json_generator_error_class, "Invalid Unicode %s at %d", code, pos);
|
|
733
254
|
}
|
|
734
255
|
|
|
735
|
-
static
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
int
|
|
739
|
-
int d2 = depth + 1;
|
|
740
|
-
long id = check_circular(a, out);
|
|
256
|
+
static const char *dump_unicode(const char *str, const char *end, Out out, const char *orig) {
|
|
257
|
+
uint32_t code = 0;
|
|
258
|
+
uint8_t b = *(uint8_t *)str;
|
|
259
|
+
int i, cnt;
|
|
741
260
|
|
|
742
|
-
if (
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
if (
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
if (
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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++ = ']';
|
|
261
|
+
if (0xC0 == (0xE0 & b)) {
|
|
262
|
+
cnt = 1;
|
|
263
|
+
code = b & 0x0000001F;
|
|
264
|
+
} else if (0xE0 == (0xF0 & b)) {
|
|
265
|
+
cnt = 2;
|
|
266
|
+
code = b & 0x0000000F;
|
|
267
|
+
} else if (0xF0 == (0xF8 & b)) {
|
|
268
|
+
cnt = 3;
|
|
269
|
+
code = b & 0x00000007;
|
|
270
|
+
} else if (0xF8 == (0xFC & b)) {
|
|
271
|
+
cnt = 4;
|
|
272
|
+
code = b & 0x00000003;
|
|
273
|
+
} else if (0xFC == (0xFE & b)) {
|
|
274
|
+
cnt = 5;
|
|
275
|
+
code = b & 0x00000001;
|
|
769
276
|
} else {
|
|
770
|
-
|
|
771
|
-
|
|
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++ = ']';
|
|
277
|
+
cnt = 0;
|
|
278
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
825
279
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
int rtype = rb_type(key);
|
|
834
|
-
|
|
835
|
-
if (rtype != T_STRING && rtype != T_SYMBOL) {
|
|
836
|
-
rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));
|
|
837
|
-
}
|
|
838
|
-
if (out->omit_nil && Qnil == value) {
|
|
839
|
-
return ST_CONTINUE;
|
|
840
|
-
}
|
|
841
|
-
if (!out->opts->dump_opts.use) {
|
|
842
|
-
size = depth * out->indent + 1;
|
|
843
|
-
if (out->end - out->cur <= size) {
|
|
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++ = ':';
|
|
853
|
-
} else {
|
|
854
|
-
size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
|
|
855
|
-
if (out->end - out->cur <= size) {
|
|
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
|
-
}
|
|
280
|
+
str++;
|
|
281
|
+
for (; 0 < cnt; cnt--, str++) {
|
|
282
|
+
b = *(uint8_t *)str;
|
|
283
|
+
if (end <= str || 0x80 != (0xC0 & b)) {
|
|
284
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
285
|
+
}
|
|
286
|
+
code = (code << 6) | (b & 0x0000003F);
|
|
887
287
|
}
|
|
888
|
-
|
|
889
|
-
|
|
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;
|
|
288
|
+
if (0x0000FFFF < code) {
|
|
289
|
+
uint32_t c1;
|
|
899
290
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
908
|
-
fill_indent(out, depth);
|
|
909
|
-
} else {
|
|
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
|
-
}
|
|
291
|
+
code -= 0x00010000;
|
|
292
|
+
c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
|
|
293
|
+
code = (code & 0x000003FF) + 0x0000DC00;
|
|
294
|
+
APPEND_CHARS(out->cur, "\\u", 2);
|
|
295
|
+
for (i = 3; 0 <= i; i--) {
|
|
296
|
+
*out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
|
|
297
|
+
}
|
|
925
298
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
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;
|
|
937
|
-
}
|
|
938
|
-
if (!out->opts->dump_opts.use) {
|
|
939
|
-
*out->cur++ = ':';
|
|
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
|
-
}
|
|
299
|
+
APPEND_CHARS(out->cur, "\\u", 2);
|
|
300
|
+
for (i = 3; 0 <= i; i--) {
|
|
301
|
+
*out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
|
|
954
302
|
}
|
|
955
|
-
|
|
956
|
-
out->depth = depth;
|
|
957
|
-
*out->cur++ = ',';
|
|
958
|
-
|
|
959
|
-
return ST_CONTINUE;
|
|
303
|
+
return str - 1;
|
|
960
304
|
}
|
|
961
305
|
|
|
962
|
-
static
|
|
963
|
-
|
|
964
|
-
int
|
|
965
|
-
long size = depth * out->indent + 1;
|
|
306
|
+
static const char *check_unicode(const char *str, const char *end, const char *orig) {
|
|
307
|
+
uint8_t b = *(uint8_t *)str;
|
|
308
|
+
int cnt = 0;
|
|
966
309
|
|
|
967
|
-
if (
|
|
968
|
-
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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);
|
|
310
|
+
if (0xC0 == (0xE0 & b)) {
|
|
311
|
+
cnt = 1;
|
|
312
|
+
} else if (0xE0 == (0xF0 & b)) {
|
|
313
|
+
cnt = 2;
|
|
314
|
+
} else if (0xF0 == (0xF8 & b)) {
|
|
315
|
+
cnt = 3;
|
|
316
|
+
} else if (0xF8 == (0xFC & b)) {
|
|
317
|
+
cnt = 4;
|
|
318
|
+
} else if (0xFC == (0xFE & b)) {
|
|
319
|
+
cnt = 5;
|
|
982
320
|
} else {
|
|
983
|
-
|
|
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++ = ']';
|
|
321
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
1021
322
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
323
|
+
str++;
|
|
324
|
+
for (; 0 < cnt; cnt--, str++) {
|
|
325
|
+
b = *(uint8_t *)str;
|
|
326
|
+
if (end <= str || 0x80 != (0xC0 & b)) {
|
|
327
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return str;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Returns 0 if not using circular references, -1 if no further writing is
|
|
334
|
+
// needed (duplicate), and a positive value if the object was added to the
|
|
335
|
+
// cache.
|
|
336
|
+
long oj_check_circular(VALUE obj, Out out) {
|
|
337
|
+
slot_t id = 0;
|
|
338
|
+
slot_t *slot;
|
|
339
|
+
|
|
340
|
+
if (Yes == out->opts->circular) {
|
|
341
|
+
if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
|
|
342
|
+
out->circ_cnt++;
|
|
343
|
+
id = out->circ_cnt;
|
|
344
|
+
*slot = id;
|
|
345
|
+
} else {
|
|
346
|
+
if (ObjectMode == out->opts->mode) {
|
|
347
|
+
assure_size(out, 18);
|
|
348
|
+
APPEND_CHARS(out->cur, "\"^r", 3);
|
|
349
|
+
dump_ulong(id, out);
|
|
350
|
+
*out->cur++ = '"';
|
|
351
|
+
}
|
|
352
|
+
return -1;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return (long)id;
|
|
1026
356
|
}
|
|
1027
357
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
if (
|
|
1043
|
-
|
|
1044
|
-
|
|
358
|
+
void oj_dump_time(VALUE obj, Out out, int withZone) {
|
|
359
|
+
char buf[64];
|
|
360
|
+
char *b = buf + sizeof(buf) - 1;
|
|
361
|
+
long size;
|
|
362
|
+
char *dot;
|
|
363
|
+
int neg = 0;
|
|
364
|
+
long one = 1000000000;
|
|
365
|
+
long long sec;
|
|
366
|
+
long long nsec;
|
|
367
|
+
|
|
368
|
+
#ifdef HAVE_RB_TIME_TIMESPEC
|
|
369
|
+
// rb_time_timespec as well as rb_time_timeeval have a bug that causes an
|
|
370
|
+
// exception to be raised if a time is before 1970 on 32 bit systems so
|
|
371
|
+
// check the timespec size and use the ruby calls if a 32 bit system.
|
|
372
|
+
if (16 <= sizeof(struct timespec)) {
|
|
373
|
+
struct timespec ts = rb_time_timespec(obj);
|
|
374
|
+
|
|
375
|
+
sec = (long long)ts.tv_sec;
|
|
376
|
+
nsec = ts.tv_nsec;
|
|
1045
377
|
} 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++ = '}';
|
|
378
|
+
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
379
|
+
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
1100
380
|
}
|
|
1101
|
-
*out->cur = '\0';
|
|
1102
|
-
}
|
|
1103
|
-
|
|
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
381
|
#else
|
|
1117
|
-
|
|
1118
|
-
|
|
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
|
|
382
|
+
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
383
|
+
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
1123
384
|
#endif
|
|
1124
|
-
|
|
385
|
+
|
|
1125
386
|
*b-- = '\0';
|
|
1126
387
|
if (withZone) {
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
388
|
+
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
|
389
|
+
int zneg = (0 > tzsecs);
|
|
390
|
+
|
|
391
|
+
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
|
392
|
+
tzsecs = 86400;
|
|
393
|
+
}
|
|
394
|
+
if (zneg) {
|
|
395
|
+
tzsecs = -tzsecs;
|
|
396
|
+
}
|
|
397
|
+
if (0 == tzsecs) {
|
|
398
|
+
*b-- = '0';
|
|
399
|
+
} else {
|
|
400
|
+
for (; 0 < tzsecs; b--, tzsecs /= 10) {
|
|
401
|
+
*b = '0' + (tzsecs % 10);
|
|
402
|
+
}
|
|
403
|
+
if (zneg) {
|
|
404
|
+
*b-- = '-';
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
*b-- = 'e';
|
|
1147
408
|
}
|
|
1148
409
|
if (0 > sec) {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
410
|
+
neg = 1;
|
|
411
|
+
sec = -sec;
|
|
412
|
+
if (0 < nsec) {
|
|
413
|
+
nsec = 1000000000 - nsec;
|
|
414
|
+
sec--;
|
|
415
|
+
}
|
|
1155
416
|
}
|
|
1156
417
|
dot = b - 9;
|
|
1157
418
|
if (0 < out->opts->sec_prec) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
419
|
+
if (9 > out->opts->sec_prec) {
|
|
420
|
+
int i;
|
|
421
|
+
|
|
422
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
|
423
|
+
dot++;
|
|
424
|
+
nsec = (nsec + 5) / 10;
|
|
425
|
+
one /= 10;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (one <= nsec) {
|
|
429
|
+
nsec -= one;
|
|
430
|
+
sec++;
|
|
431
|
+
}
|
|
432
|
+
for (; dot < b; b--, nsec /= 10) {
|
|
433
|
+
*b = '0' + (nsec % 10);
|
|
434
|
+
}
|
|
435
|
+
*b-- = '.';
|
|
1175
436
|
}
|
|
1176
437
|
if (0 == sec) {
|
|
1177
|
-
|
|
438
|
+
*b-- = '0';
|
|
1178
439
|
} else {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
440
|
+
for (; 0 < sec; b--, sec /= 10) {
|
|
441
|
+
*b = '0' + (sec % 10);
|
|
442
|
+
}
|
|
1182
443
|
}
|
|
1183
444
|
if (neg) {
|
|
1184
|
-
|
|
445
|
+
*b-- = '-';
|
|
1185
446
|
}
|
|
1186
447
|
b++;
|
|
1187
448
|
size = sizeof(buf) - (b - buf) - 1;
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
}
|
|
1191
|
-
memcpy(out->cur, b, size);
|
|
1192
|
-
out->cur += size;
|
|
449
|
+
assure_size(out, size);
|
|
450
|
+
APPEND_CHARS(out->cur, b, size);
|
|
1193
451
|
*out->cur = '\0';
|
|
1194
452
|
}
|
|
1195
453
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
454
|
+
void oj_dump_ruby_time(VALUE obj, Out out) {
|
|
455
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
1199
456
|
|
|
1200
|
-
|
|
457
|
+
oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
|
1201
458
|
}
|
|
1202
459
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
460
|
+
void oj_dump_xml_time(VALUE obj, Out out) {
|
|
461
|
+
char buf[64];
|
|
462
|
+
struct _timeInfo ti;
|
|
463
|
+
long one = 1000000000;
|
|
464
|
+
int64_t sec;
|
|
465
|
+
long long nsec;
|
|
466
|
+
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
|
467
|
+
int tzhour, tzmin;
|
|
468
|
+
char tzsign = '+';
|
|
469
|
+
|
|
470
|
+
#ifdef HAVE_RB_TIME_TIMESPEC
|
|
471
|
+
if (16 <= sizeof(struct timespec)) {
|
|
472
|
+
struct timespec ts = rb_time_timespec(obj);
|
|
473
|
+
|
|
474
|
+
sec = ts.tv_sec;
|
|
475
|
+
nsec = ts.tv_nsec;
|
|
476
|
+
} else {
|
|
477
|
+
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
478
|
+
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
479
|
+
}
|
|
1216
480
|
#else
|
|
1217
|
-
|
|
481
|
+
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
|
482
|
+
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
|
1218
483
|
#endif
|
|
1219
|
-
#endif
|
|
1220
|
-
long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
|
|
1221
|
-
int tzhour, tzmin;
|
|
1222
|
-
char tzsign = '+';
|
|
1223
484
|
|
|
1224
|
-
|
|
1225
|
-
grow(out, 36);
|
|
1226
|
-
}
|
|
485
|
+
assure_size(out, 36);
|
|
1227
486
|
if (9 > out->opts->sec_prec) {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
487
|
+
int i;
|
|
488
|
+
|
|
489
|
+
// This is pretty lame but to be compatible with rails and active
|
|
490
|
+
// support rounding is not done but instead a floor is done when
|
|
491
|
+
// second precision is 3 just to be like rails. sigh.
|
|
492
|
+
if (3 == out->opts->sec_prec) {
|
|
493
|
+
nsec /= 1000000;
|
|
494
|
+
one = 1000;
|
|
495
|
+
} else {
|
|
496
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
|
497
|
+
nsec = (nsec + 5) / 10;
|
|
498
|
+
one /= 10;
|
|
499
|
+
}
|
|
500
|
+
if (one <= nsec) {
|
|
501
|
+
nsec -= one;
|
|
502
|
+
sec++;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
1238
505
|
}
|
|
1239
506
|
// 2012-01-05T23:58:07.123456000+09:00
|
|
1240
|
-
//tm = localtime(&sec);
|
|
507
|
+
// tm = localtime(&sec);
|
|
1241
508
|
sec += tzsecs;
|
|
1242
|
-
|
|
1243
|
-
#if 1
|
|
509
|
+
sec_as_time((int64_t)sec, &ti);
|
|
1244
510
|
if (0 > tzsecs) {
|
|
1245
511
|
tzsign = '-';
|
|
1246
512
|
tzhour = (int)(tzsecs / -3600);
|
|
1247
|
-
tzmin
|
|
513
|
+
tzmin = (int)(tzsecs / -60) - (tzhour * 60);
|
|
1248
514
|
} else {
|
|
1249
515
|
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
|
-
}
|
|
516
|
+
tzmin = (int)(tzsecs / 60) - (tzhour * 60);
|
|
517
|
+
}
|
|
518
|
+
if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
|
|
519
|
+
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
|
|
520
|
+
int len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
|
|
521
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
|
522
|
+
} else {
|
|
523
|
+
int len = sprintf(buf,
|
|
524
|
+
"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
|
|
525
|
+
ti.year,
|
|
526
|
+
ti.mon,
|
|
527
|
+
ti.day,
|
|
528
|
+
ti.hour,
|
|
529
|
+
ti.min,
|
|
530
|
+
ti.sec,
|
|
531
|
+
tzsign,
|
|
532
|
+
tzhour,
|
|
533
|
+
tzmin);
|
|
534
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
|
535
|
+
}
|
|
1275
536
|
} 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), 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), 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), 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), 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), 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), 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), RSTRING_LEN(rstr), out);
|
|
1455
|
-
} else {
|
|
1456
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), 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), 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), RSTRING_LEN(rstr), out);
|
|
1537
|
-
} else {
|
|
1538
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), 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), 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), 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);
|
|
537
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
|
|
538
|
+
int len;
|
|
539
|
+
|
|
540
|
+
if (9 > out->opts->sec_prec) {
|
|
541
|
+
format[32] = '0' + out->opts->sec_prec;
|
|
542
|
+
}
|
|
543
|
+
len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
|
|
544
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
|
1617
545
|
} else {
|
|
1618
|
-
|
|
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), 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), 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), 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 = 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
|
-
}
|
|
2053
|
-
} else {
|
|
2054
|
-
size = d2 * out->indent + 1;
|
|
2055
|
-
for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
|
|
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
|
-
}
|
|
2107
|
-
|
|
2108
|
-
static void
|
|
2109
|
-
raise_strict(VALUE obj) {
|
|
2110
|
-
rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.\n", rb_class2name(rb_obj_class(obj)));
|
|
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);
|
|
546
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
|
|
547
|
+
int len;
|
|
2116
548
|
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
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), 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
|
-
}
|
|
549
|
+
if (9 > out->opts->sec_prec) {
|
|
550
|
+
format[32] = '0' + out->opts->sec_prec;
|
|
551
|
+
}
|
|
552
|
+
len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
|
|
553
|
+
oj_dump_cstr(buf, len, 0, 0, out);
|
|
2233
554
|
}
|
|
2234
555
|
}
|
|
2235
556
|
|
|
2236
|
-
void
|
|
2237
|
-
oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
|
|
557
|
+
void oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
|
|
2238
558
|
oj_dump_obj_to_json_using_params(obj, copts, out, 0, 0);
|
|
2239
559
|
}
|
|
2240
560
|
|
|
2241
|
-
void
|
|
2242
|
-
oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
|
|
561
|
+
void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
|
|
2243
562
|
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;
|
|
563
|
+
oj_out_init(out);
|
|
2247
564
|
}
|
|
2248
|
-
out->cur = out->buf;
|
|
2249
565
|
out->circ_cnt = 0;
|
|
2250
|
-
out->opts
|
|
566
|
+
out->opts = copts;
|
|
2251
567
|
out->hash_cnt = 0;
|
|
568
|
+
out->indent = copts->indent;
|
|
569
|
+
out->argc = argc;
|
|
570
|
+
out->argv = argv;
|
|
571
|
+
out->ropts = NULL;
|
|
2252
572
|
if (Yes == copts->circular) {
|
|
2253
|
-
|
|
573
|
+
oj_cache8_new(&out->circ_cache);
|
|
574
|
+
}
|
|
575
|
+
switch (copts->mode) {
|
|
576
|
+
case StrictMode: oj_dump_strict_val(obj, 0, out); break;
|
|
577
|
+
case NullMode: oj_dump_null_val(obj, 0, out); break;
|
|
578
|
+
case ObjectMode: oj_dump_obj_val(obj, 0, out); break;
|
|
579
|
+
case CompatMode: oj_dump_compat_val(obj, 0, out, Yes == copts->to_json); break;
|
|
580
|
+
case RailsMode: oj_dump_rails_val(obj, 0, out); break;
|
|
581
|
+
case CustomMode: oj_dump_custom_val(obj, 0, out, true); break;
|
|
582
|
+
case WabMode: oj_dump_wab_val(obj, 0, out); break;
|
|
583
|
+
default: oj_dump_custom_val(obj, 0, out, true); break;
|
|
2254
584
|
}
|
|
2255
|
-
out->indent = copts->indent;
|
|
2256
|
-
dump_val(obj, 0, out, argc, argv, true);
|
|
2257
585
|
if (0 < out->indent) {
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
default:
|
|
2264
|
-
break;
|
|
2265
|
-
}
|
|
586
|
+
switch (*(out->cur - 1)) {
|
|
587
|
+
case ']':
|
|
588
|
+
case '}': assure_size(out, 1); *out->cur++ = '\n';
|
|
589
|
+
default: break;
|
|
590
|
+
}
|
|
2266
591
|
}
|
|
2267
592
|
*out->cur = '\0';
|
|
2268
593
|
if (Yes == copts->circular) {
|
|
2269
|
-
|
|
594
|
+
oj_cache8_delete(out->circ_cache);
|
|
2270
595
|
}
|
|
2271
596
|
}
|
|
2272
597
|
|
|
2273
|
-
void
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
out.
|
|
2282
|
-
out.end = buf + sizeof(buf) - BUFFER_EXTRA;
|
|
2283
|
-
out.allocated = 0;
|
|
2284
|
-
out.omit_nil = copts->dump_opts.omit_nil;
|
|
598
|
+
void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
|
|
599
|
+
struct _out out;
|
|
600
|
+
size_t size;
|
|
601
|
+
FILE *f;
|
|
602
|
+
int ok;
|
|
603
|
+
|
|
604
|
+
oj_out_init(&out);
|
|
605
|
+
|
|
606
|
+
out.omit_nil = copts->dump_opts.omit_nil;
|
|
2285
607
|
oj_dump_obj_to_json(obj, copts, &out);
|
|
2286
608
|
size = out.cur - out.buf;
|
|
2287
609
|
if (0 == (f = fopen(path, "w"))) {
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
}
|
|
2291
|
-
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
|
610
|
+
oj_out_free(&out);
|
|
611
|
+
rb_raise(rb_eIOError, "%s", strerror(errno));
|
|
2292
612
|
}
|
|
2293
613
|
ok = (size == fwrite(out.buf, 1, size, f));
|
|
2294
|
-
|
|
2295
|
-
|
|
614
|
+
|
|
615
|
+
oj_out_free(&out);
|
|
616
|
+
|
|
617
|
+
if (!ok) {
|
|
618
|
+
int err = ferror(f);
|
|
619
|
+
fclose(f);
|
|
620
|
+
|
|
621
|
+
rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
|
|
2296
622
|
}
|
|
2297
623
|
fclose(f);
|
|
2298
|
-
|
|
2299
|
-
int err = ferror(f);
|
|
624
|
+
}
|
|
2300
625
|
|
|
2301
|
-
|
|
626
|
+
#if !IS_WINDOWS
|
|
627
|
+
static void write_ready(int fd) {
|
|
628
|
+
struct pollfd pp;
|
|
629
|
+
int i;
|
|
630
|
+
|
|
631
|
+
pp.fd = fd;
|
|
632
|
+
pp.events = POLLERR | POLLOUT;
|
|
633
|
+
pp.revents = 0;
|
|
634
|
+
if (0 >= (i = poll(&pp, 1, 5000))) {
|
|
635
|
+
if (0 == i || EAGAIN == errno) {
|
|
636
|
+
rb_raise(rb_eIOError, "write timed out");
|
|
637
|
+
}
|
|
638
|
+
rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
|
|
2302
639
|
}
|
|
2303
640
|
}
|
|
641
|
+
#endif
|
|
2304
642
|
|
|
2305
|
-
void
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
ssize_t size;
|
|
2310
|
-
VALUE clas = rb_obj_class(stream);
|
|
643
|
+
void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
|
|
644
|
+
struct _out out;
|
|
645
|
+
ssize_t size;
|
|
646
|
+
VALUE clas = rb_obj_class(stream);
|
|
2311
647
|
#if !IS_WINDOWS
|
|
2312
|
-
int
|
|
2313
|
-
VALUE
|
|
648
|
+
int fd;
|
|
649
|
+
VALUE s;
|
|
2314
650
|
#endif
|
|
2315
651
|
|
|
2316
|
-
out
|
|
2317
|
-
|
|
2318
|
-
out.
|
|
2319
|
-
out.omit_nil = copts->dump_opts.omit_nil;
|
|
652
|
+
oj_out_init(&out);
|
|
653
|
+
|
|
654
|
+
out.omit_nil = copts->dump_opts.omit_nil;
|
|
2320
655
|
oj_dump_obj_to_json(obj, copts, &out);
|
|
2321
656
|
size = out.cur - out.buf;
|
|
2322
657
|
if (oj_stringio_class == clas) {
|
|
2323
|
-
|
|
658
|
+
rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
|
|
2324
659
|
#if !IS_WINDOWS
|
|
2325
|
-
} else if (rb_respond_to(stream, oj_fileno_id) &&
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
660
|
+
} else if (rb_respond_to(stream, oj_fileno_id) && Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
|
|
661
|
+
0 != (fd = FIX2INT(s))) {
|
|
662
|
+
ssize_t cnt;
|
|
663
|
+
ssize_t total = 0;
|
|
664
|
+
|
|
665
|
+
while (true) {
|
|
666
|
+
if (0 > (cnt = write(fd, out.buf + total, size - total))) {
|
|
667
|
+
if (EAGAIN != errno) {
|
|
668
|
+
rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
total += cnt;
|
|
673
|
+
if (size <= total) {
|
|
674
|
+
// Completed
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
write_ready(fd);
|
|
678
|
+
}
|
|
2334
679
|
#endif
|
|
2335
680
|
} else if (rb_respond_to(stream, oj_write_id)) {
|
|
2336
|
-
|
|
681
|
+
rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
|
|
2337
682
|
} else {
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
}
|
|
2341
|
-
rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
|
|
2342
|
-
}
|
|
2343
|
-
if (out.allocated) {
|
|
2344
|
-
xfree(out.buf);
|
|
683
|
+
oj_out_free(&out);
|
|
684
|
+
rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
|
|
2345
685
|
}
|
|
686
|
+
oj_out_free(&out);
|
|
2346
687
|
}
|
|
2347
688
|
|
|
2348
|
-
|
|
689
|
+
void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
|
|
690
|
+
int idx = RB_ENCODING_GET(obj);
|
|
2349
691
|
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
grow(out, size);
|
|
692
|
+
if (oj_utf8_encoding_index != idx) {
|
|
693
|
+
rb_encoding *enc = rb_enc_from_index(idx);
|
|
694
|
+
obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
|
|
2354
695
|
}
|
|
2355
|
-
|
|
2356
|
-
out->cur += size;
|
|
2357
|
-
*out->cur = '\0';
|
|
696
|
+
oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
|
|
2358
697
|
}
|
|
2359
698
|
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
switch (leaf->value_type) {
|
|
2363
|
-
case STR_VAL:
|
|
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), 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
|
-
}
|
|
2374
|
-
}
|
|
699
|
+
void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
|
|
700
|
+
volatile VALUE s = rb_sym2str(obj);
|
|
2375
701
|
|
|
2376
|
-
|
|
2377
|
-
dump_leaf_fixnum(Leaf leaf, Out out) {
|
|
2378
|
-
switch (leaf->value_type) {
|
|
2379
|
-
case STR_VAL:
|
|
2380
|
-
dump_chars(leaf->str, strlen(leaf->str), out);
|
|
2381
|
-
break;
|
|
2382
|
-
case RUBY_VAL:
|
|
2383
|
-
if (T_BIGNUM == rb_type(leaf->value)) {
|
|
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;
|
|
2393
|
-
}
|
|
702
|
+
oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 0, 0, out);
|
|
2394
703
|
}
|
|
2395
704
|
|
|
2396
|
-
static void
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
break;
|
|
2405
|
-
case COL_VAL:
|
|
2406
|
-
default:
|
|
2407
|
-
rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
|
|
2408
|
-
break;
|
|
705
|
+
static void debug_raise(const char *orig, size_t cnt, int line) {
|
|
706
|
+
char buf[1024];
|
|
707
|
+
char *b = buf;
|
|
708
|
+
const char *s = orig;
|
|
709
|
+
const char *s_end = s + cnt;
|
|
710
|
+
|
|
711
|
+
if (32 < s_end - s) {
|
|
712
|
+
s_end = s + 32;
|
|
2409
713
|
}
|
|
714
|
+
for (; s < s_end; s++) {
|
|
715
|
+
b += sprintf(b, " %02x", *s);
|
|
716
|
+
}
|
|
717
|
+
*b = '\0';
|
|
718
|
+
rb_raise(oj_json_generator_error_class, "Partial character in string. %s @ %d", buf, line);
|
|
2410
719
|
}
|
|
2411
720
|
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
721
|
+
void oj_dump_raw_json(VALUE obj, int depth, Out out) {
|
|
722
|
+
if (oj_string_writer_class == rb_obj_class(obj)) {
|
|
723
|
+
StrWriter sw = (StrWriter)DATA_PTR(obj);
|
|
724
|
+
size_t len = sw->out.cur - sw->out.buf;
|
|
2416
725
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
*out->cur++ = '[';
|
|
2422
|
-
if (0 == leaf->elements) {
|
|
2423
|
-
*out->cur++ = ']';
|
|
726
|
+
if (0 < len) {
|
|
727
|
+
len--;
|
|
728
|
+
}
|
|
729
|
+
oj_dump_raw(sw->out.buf, len, out);
|
|
2424
730
|
} else {
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
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++ = ']';
|
|
731
|
+
volatile VALUE jv;
|
|
732
|
+
|
|
733
|
+
if (Yes == out->opts->trace) {
|
|
734
|
+
oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
|
|
735
|
+
}
|
|
736
|
+
jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
|
|
737
|
+
if (Yes == out->opts->trace) {
|
|
738
|
+
oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
|
|
739
|
+
}
|
|
740
|
+
oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
|
|
2446
741
|
}
|
|
2447
|
-
*out->cur = '\0';
|
|
2448
742
|
}
|
|
2449
743
|
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
744
|
+
void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
|
|
745
|
+
size_t size;
|
|
746
|
+
char *cmap;
|
|
747
|
+
const char *orig = str;
|
|
748
|
+
bool has_hi = false;
|
|
2454
749
|
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
750
|
+
switch (out->opts->escape_mode) {
|
|
751
|
+
case NLEsc:
|
|
752
|
+
cmap = newline_friendly_chars;
|
|
753
|
+
size = newline_friendly_size((uint8_t *)str, cnt);
|
|
754
|
+
break;
|
|
755
|
+
case ASCIIEsc:
|
|
756
|
+
cmap = ascii_friendly_chars;
|
|
757
|
+
size = ascii_friendly_size((uint8_t *)str, cnt);
|
|
758
|
+
break;
|
|
759
|
+
case XSSEsc:
|
|
760
|
+
cmap = xss_friendly_chars;
|
|
761
|
+
size = xss_friendly_size((uint8_t *)str, cnt);
|
|
762
|
+
break;
|
|
763
|
+
case JXEsc:
|
|
764
|
+
cmap = hixss_friendly_chars;
|
|
765
|
+
size = hixss_friendly_size((uint8_t *)str, cnt);
|
|
766
|
+
break;
|
|
767
|
+
case RailsXEsc: {
|
|
768
|
+
long sz;
|
|
769
|
+
|
|
770
|
+
cmap = rails_xss_friendly_chars;
|
|
771
|
+
sz = rails_xss_friendly_size((uint8_t *)str, cnt);
|
|
772
|
+
if (sz < 0) {
|
|
773
|
+
has_hi = true;
|
|
774
|
+
size = (size_t)-sz;
|
|
775
|
+
} else {
|
|
776
|
+
size = (size_t)sz;
|
|
777
|
+
}
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
case RailsEsc:
|
|
781
|
+
cmap = rails_friendly_chars;
|
|
782
|
+
size = rails_friendly_size((uint8_t *)str, cnt);
|
|
783
|
+
break;
|
|
784
|
+
case JSONEsc:
|
|
785
|
+
default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
|
|
2458
786
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
787
|
+
assure_size(out, size + BUFFER_EXTRA);
|
|
788
|
+
*out->cur++ = '"';
|
|
789
|
+
|
|
790
|
+
if (escape1) {
|
|
791
|
+
APPEND_CHARS(out->cur, "\\u00", 4);
|
|
792
|
+
dump_hex((uint8_t)*str, out);
|
|
793
|
+
cnt--;
|
|
794
|
+
size--;
|
|
795
|
+
str++;
|
|
796
|
+
is_sym = 0; // just to make sure
|
|
797
|
+
}
|
|
798
|
+
if (cnt == size && !has_hi) {
|
|
799
|
+
if (is_sym) {
|
|
800
|
+
*out->cur++ = ':';
|
|
801
|
+
}
|
|
802
|
+
APPEND_CHARS(out->cur, str, cnt);
|
|
803
|
+
*out->cur++ = '"';
|
|
2462
804
|
} else {
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
805
|
+
const char *end = str + cnt;
|
|
806
|
+
const char *check_start = str;
|
|
807
|
+
|
|
808
|
+
if (is_sym) {
|
|
809
|
+
*out->cur++ = ':';
|
|
810
|
+
}
|
|
811
|
+
for (; str < end; str++) {
|
|
812
|
+
switch (cmap[(uint8_t)*str]) {
|
|
813
|
+
case '1':
|
|
814
|
+
if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && check_start <= str) {
|
|
815
|
+
if (0 != (0x80 & (uint8_t)*str)) {
|
|
816
|
+
if (0xC0 == (0xC0 & (uint8_t)*str)) {
|
|
817
|
+
check_start = check_unicode(str, end, orig);
|
|
818
|
+
} else {
|
|
819
|
+
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
*out->cur++ = *str;
|
|
824
|
+
break;
|
|
825
|
+
case '2':
|
|
826
|
+
*out->cur++ = '\\';
|
|
827
|
+
switch (*str) {
|
|
828
|
+
case '\\': *out->cur++ = '\\'; break;
|
|
829
|
+
case '\b': *out->cur++ = 'b'; break;
|
|
830
|
+
case '\t': *out->cur++ = 't'; break;
|
|
831
|
+
case '\n': *out->cur++ = 'n'; break;
|
|
832
|
+
case '\f': *out->cur++ = 'f'; break;
|
|
833
|
+
case '\r': *out->cur++ = 'r'; break;
|
|
834
|
+
default: *out->cur++ = *str; break;
|
|
835
|
+
}
|
|
836
|
+
break;
|
|
837
|
+
case '3': // Unicode
|
|
838
|
+
if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
|
|
839
|
+
2 <= end - str) {
|
|
840
|
+
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
|
841
|
+
str = dump_unicode(str, end, out, orig);
|
|
842
|
+
} else {
|
|
843
|
+
check_start = check_unicode(str, end, orig);
|
|
844
|
+
*out->cur++ = *str;
|
|
845
|
+
}
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
str = dump_unicode(str, end, out, orig);
|
|
849
|
+
break;
|
|
850
|
+
case '6': // control characters
|
|
851
|
+
if (*(uint8_t *)str < 0x80) {
|
|
852
|
+
APPEND_CHARS(out->cur, "\\u00", 4);
|
|
853
|
+
dump_hex((uint8_t)*str, out);
|
|
854
|
+
} else {
|
|
855
|
+
if (0xe2 == (uint8_t)*str &&
|
|
856
|
+
(JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
|
|
857
|
+
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
|
858
|
+
str = dump_unicode(str, end, out, orig);
|
|
859
|
+
} else {
|
|
860
|
+
check_start = check_unicode(str, end, orig);
|
|
861
|
+
*out->cur++ = *str;
|
|
862
|
+
}
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
str = dump_unicode(str, end, out, orig);
|
|
866
|
+
}
|
|
867
|
+
break;
|
|
868
|
+
default: break; // ignore, should never happen if the table is correct
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
*out->cur++ = '"';
|
|
872
|
+
}
|
|
873
|
+
if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 0 < str - orig &&
|
|
874
|
+
0 != (0x80 & *(str - 1))) {
|
|
875
|
+
uint8_t c = (uint8_t) * (str - 1);
|
|
876
|
+
int i;
|
|
877
|
+
int scnt = (int)(str - orig);
|
|
878
|
+
|
|
879
|
+
// Last utf-8 characters must be 0x10xxxxxx. The start must be
|
|
880
|
+
// 0x110xxxxx for 2 characters, 0x1110xxxx for 3, and 0x11110xxx for
|
|
881
|
+
// 4.
|
|
882
|
+
if (0 != (0x40 & c)) {
|
|
883
|
+
debug_raise(orig, cnt, __LINE__);
|
|
884
|
+
}
|
|
885
|
+
for (i = 1; i < (int)scnt && i < 4; i++) {
|
|
886
|
+
c = str[-1 - i];
|
|
887
|
+
if (0x80 != (0xC0 & c)) {
|
|
888
|
+
switch (i) {
|
|
889
|
+
case 1:
|
|
890
|
+
if (0xC0 != (0xE0 & c)) {
|
|
891
|
+
debug_raise(orig, cnt, __LINE__);
|
|
892
|
+
}
|
|
893
|
+
break;
|
|
894
|
+
case 2:
|
|
895
|
+
if (0xE0 != (0xF0 & c)) {
|
|
896
|
+
debug_raise(orig, cnt, __LINE__);
|
|
897
|
+
}
|
|
898
|
+
break;
|
|
899
|
+
case 3:
|
|
900
|
+
if (0xF0 != (0xF8 & c)) {
|
|
901
|
+
debug_raise(orig, cnt, __LINE__);
|
|
902
|
+
}
|
|
903
|
+
break;
|
|
904
|
+
default: // can't get here
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (i == (int)scnt || 4 <= i) {
|
|
911
|
+
debug_raise(orig, cnt, __LINE__);
|
|
912
|
+
}
|
|
2486
913
|
}
|
|
2487
914
|
*out->cur = '\0';
|
|
2488
915
|
}
|
|
2489
916
|
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
switch (leaf->rtype) {
|
|
2493
|
-
case T_NIL:
|
|
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
|
-
}
|
|
2521
|
-
}
|
|
917
|
+
void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
|
|
918
|
+
const char *s = rb_class2name(obj);
|
|
2522
919
|
|
|
2523
|
-
|
|
2524
|
-
oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out) {
|
|
2525
|
-
if (0 == out->buf) {
|
|
2526
|
-
out->buf = ALLOC_N(char, 4096);
|
|
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);
|
|
920
|
+
oj_dump_cstr(s, strlen(s), 0, 0, out);
|
|
2536
921
|
}
|
|
2537
922
|
|
|
2538
|
-
void
|
|
2539
|
-
|
|
2540
|
-
char buf[4096];
|
|
2541
|
-
struct _Out out;
|
|
2542
|
-
size_t size;
|
|
2543
|
-
FILE *f;
|
|
2544
|
-
|
|
2545
|
-
out.buf = buf;
|
|
2546
|
-
out.end = buf + sizeof(buf) - BUFFER_EXTRA;
|
|
2547
|
-
out.allocated = 0;
|
|
2548
|
-
out.omit_nil = copts->dump_opts.omit_nil;
|
|
2549
|
-
oj_dump_leaf_to_json(leaf, copts, &out);
|
|
2550
|
-
size = out.cur - out.buf;
|
|
2551
|
-
if (0 == (f = fopen(path, "w"))) {
|
|
2552
|
-
rb_raise(rb_eIOError, "%s\n", strerror(errno));
|
|
2553
|
-
}
|
|
2554
|
-
if (size != fwrite(out.buf, 1, size, f)) {
|
|
2555
|
-
int err = ferror(f);
|
|
923
|
+
void oj_dump_obj_to_s(VALUE obj, Out out) {
|
|
924
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
2556
925
|
|
|
2557
|
-
|
|
2558
|
-
}
|
|
2559
|
-
if (out.allocated) {
|
|
2560
|
-
xfree(out.buf);
|
|
2561
|
-
}
|
|
2562
|
-
fclose(f);
|
|
926
|
+
oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
|
|
2563
927
|
}
|
|
2564
928
|
|
|
2565
|
-
|
|
929
|
+
void oj_dump_raw(const char *str, size_t cnt, Out out) {
|
|
930
|
+
assure_size(out, cnt + 10);
|
|
931
|
+
APPEND_CHARS(out->cur, str, cnt);
|
|
932
|
+
*out->cur = '\0';
|
|
933
|
+
}
|
|
2566
934
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
935
|
+
void oj_out_init(Out out) {
|
|
936
|
+
out->buf = out->stack_buffer;
|
|
937
|
+
out->cur = out->buf;
|
|
938
|
+
out->end = out->buf + sizeof(out->stack_buffer) - BUFFER_EXTRA;
|
|
939
|
+
out->allocated = false;
|
|
940
|
+
}
|
|
2570
941
|
|
|
2571
|
-
|
|
2572
|
-
|
|
942
|
+
void oj_out_free(Out out) {
|
|
943
|
+
if (out->allocated) {
|
|
944
|
+
xfree(out->buf); // TBD
|
|
2573
945
|
}
|
|
2574
946
|
}
|
|
2575
947
|
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
948
|
+
void oj_grow_out(Out out, size_t len) {
|
|
949
|
+
size_t size = out->end - out->buf;
|
|
950
|
+
long pos = out->cur - out->buf;
|
|
951
|
+
char *buf = out->buf;
|
|
2580
952
|
|
|
2581
|
-
|
|
2582
|
-
|
|
953
|
+
size *= 2;
|
|
954
|
+
if (size <= len * 2 + pos) {
|
|
955
|
+
size += len;
|
|
956
|
+
}
|
|
957
|
+
if (out->allocated) {
|
|
958
|
+
REALLOC_N(buf, char, (size + BUFFER_EXTRA));
|
|
959
|
+
} else {
|
|
960
|
+
buf = ALLOC_N(char, (size + BUFFER_EXTRA));
|
|
961
|
+
out->allocated = true;
|
|
962
|
+
memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
|
|
2583
963
|
}
|
|
2584
|
-
|
|
2585
|
-
|
|
964
|
+
if (0 == buf) {
|
|
965
|
+
rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]", ENOSPC, strerror(ENOSPC));
|
|
966
|
+
}
|
|
967
|
+
out->buf = buf;
|
|
968
|
+
out->end = buf + size;
|
|
969
|
+
out->cur = out->buf + pos;
|
|
2586
970
|
}
|
|
2587
971
|
|
|
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;
|
|
2602
|
-
}
|
|
972
|
+
void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
|
|
973
|
+
assure_size(out, 4);
|
|
974
|
+
APPEND_CHARS(out->cur, "null", 4);
|
|
975
|
+
*out->cur = '\0';
|
|
2603
976
|
}
|
|
2604
977
|
|
|
2605
|
-
void
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
978
|
+
void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
|
|
979
|
+
assure_size(out, 4);
|
|
980
|
+
APPEND_CHARS(out->cur, "true", 4);
|
|
981
|
+
*out->cur = '\0';
|
|
982
|
+
}
|
|
2609
983
|
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
rb_raise(rb_eStandardError, "Can only push a key onto an Object.");
|
|
2615
|
-
}
|
|
2616
|
-
size = sw->depth * sw->out.indent + 3;
|
|
2617
|
-
if (sw->out.end - sw->out.cur <= (long)size) {
|
|
2618
|
-
grow(&sw->out, size);
|
|
2619
|
-
}
|
|
2620
|
-
maybe_comma(sw);
|
|
2621
|
-
if (0 < sw->depth) {
|
|
2622
|
-
fill_indent(&sw->out, sw->depth);
|
|
2623
|
-
}
|
|
2624
|
-
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
|
2625
|
-
*sw->out.cur++ = ':';
|
|
2626
|
-
sw->keyWritten = 1;
|
|
984
|
+
void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
|
|
985
|
+
assure_size(out, 5);
|
|
986
|
+
APPEND_CHARS(out->cur, "false", 5);
|
|
987
|
+
*out->cur = '\0';
|
|
2627
988
|
}
|
|
2628
989
|
|
|
2629
|
-
void
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
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
|
-
}
|
|
990
|
+
void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
991
|
+
char buf[32];
|
|
992
|
+
char * b = buf + sizeof(buf) - 1;
|
|
993
|
+
long long num = NUM2LL(obj);
|
|
994
|
+
int neg = 0;
|
|
995
|
+
size_t cnt = 0;
|
|
996
|
+
bool dump_as_string = false;
|
|
997
|
+
|
|
998
|
+
if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
|
|
999
|
+
(out->opts->int_range_max < num || out->opts->int_range_min > num)) {
|
|
1000
|
+
dump_as_string = true;
|
|
2652
1001
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
1002
|
+
if (0 > num) {
|
|
1003
|
+
neg = 1;
|
|
1004
|
+
num = -num;
|
|
1005
|
+
}
|
|
1006
|
+
*b-- = '\0';
|
|
2656
1007
|
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
1008
|
+
if (dump_as_string) {
|
|
1009
|
+
*b-- = '"';
|
|
1010
|
+
}
|
|
1011
|
+
if (0 < num) {
|
|
1012
|
+
for (; 0 < num; num /= 10, b--) {
|
|
1013
|
+
*b = (num % 10) + '0';
|
|
1014
|
+
}
|
|
1015
|
+
if (neg) {
|
|
1016
|
+
*b = '-';
|
|
1017
|
+
} else {
|
|
1018
|
+
b++;
|
|
1019
|
+
}
|
|
2664
1020
|
} else {
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
if (sw->out.end - sw->out.cur <= size) {
|
|
2670
|
-
grow(&sw->out, size);
|
|
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
|
-
}
|
|
1021
|
+
*b = '0';
|
|
1022
|
+
}
|
|
1023
|
+
if (dump_as_string) {
|
|
1024
|
+
*--b = '"';
|
|
2680
1025
|
}
|
|
2681
|
-
|
|
2682
|
-
|
|
1026
|
+
cnt = sizeof(buf) - (b - buf) - 1;
|
|
1027
|
+
assure_size(out, cnt);
|
|
1028
|
+
APPEND_CHARS(out->cur, b, cnt);
|
|
1029
|
+
*out->cur = '\0';
|
|
2683
1030
|
}
|
|
2684
1031
|
|
|
2685
|
-
void
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
1032
|
+
void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1033
|
+
volatile VALUE rs = rb_big2str(obj, 10);
|
|
1034
|
+
int cnt = (int)RSTRING_LEN(rs);
|
|
1035
|
+
bool dump_as_string = false;
|
|
1036
|
+
|
|
1037
|
+
if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
|
|
1038
|
+
dump_as_string = true;
|
|
1039
|
+
assure_size(out, cnt + 2);
|
|
1040
|
+
*out->cur++ = '"';
|
|
2689
1041
|
} else {
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
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
|
-
}
|
|
1042
|
+
assure_size(out, cnt);
|
|
1043
|
+
}
|
|
1044
|
+
APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
|
|
1045
|
+
if (dump_as_string) {
|
|
1046
|
+
*out->cur++ = '"';
|
|
2705
1047
|
}
|
|
2706
|
-
|
|
1048
|
+
*out->cur = '\0';
|
|
2707
1049
|
}
|
|
2708
1050
|
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
1051
|
+
// Removed dependencies on math due to problems with CentOS 5.4.
|
|
1052
|
+
void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1053
|
+
char buf[64];
|
|
1054
|
+
char *b;
|
|
1055
|
+
double d = rb_num2dbl(obj);
|
|
1056
|
+
int cnt = 0;
|
|
1057
|
+
|
|
1058
|
+
if (0.0 == d) {
|
|
1059
|
+
b = buf;
|
|
1060
|
+
*b++ = '0';
|
|
1061
|
+
*b++ = '.';
|
|
1062
|
+
*b++ = '0';
|
|
1063
|
+
*b++ = '\0';
|
|
1064
|
+
cnt = 3;
|
|
1065
|
+
} else if (OJ_INFINITY == d) {
|
|
1066
|
+
if (ObjectMode == out->opts->mode) {
|
|
1067
|
+
strcpy(buf, inf_val);
|
|
1068
|
+
cnt = sizeof(inf_val) - 1;
|
|
1069
|
+
} else {
|
|
1070
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
|
1071
|
+
|
|
1072
|
+
if (AutoNan == nd) {
|
|
1073
|
+
switch (out->opts->mode) {
|
|
1074
|
+
case CompatMode: nd = WordNan; break;
|
|
1075
|
+
case StrictMode: nd = RaiseNan; break;
|
|
1076
|
+
case NullMode: nd = NullNan; break;
|
|
1077
|
+
case CustomMode: nd = NullNan; break;
|
|
1078
|
+
default: break;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
switch (nd) {
|
|
1082
|
+
case RaiseNan: raise_strict(obj); break;
|
|
1083
|
+
case WordNan:
|
|
1084
|
+
strcpy(buf, "Infinity");
|
|
1085
|
+
cnt = 8;
|
|
1086
|
+
break;
|
|
1087
|
+
case NullNan:
|
|
1088
|
+
strcpy(buf, "null");
|
|
1089
|
+
cnt = 4;
|
|
1090
|
+
break;
|
|
1091
|
+
case HugeNan:
|
|
1092
|
+
default:
|
|
1093
|
+
strcpy(buf, inf_val);
|
|
1094
|
+
cnt = sizeof(inf_val) - 1;
|
|
1095
|
+
break;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
} else if (-OJ_INFINITY == d) {
|
|
1099
|
+
if (ObjectMode == out->opts->mode) {
|
|
1100
|
+
strcpy(buf, ninf_val);
|
|
1101
|
+
cnt = sizeof(ninf_val) - 1;
|
|
1102
|
+
} else {
|
|
1103
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
|
1104
|
+
|
|
1105
|
+
if (AutoNan == nd) {
|
|
1106
|
+
switch (out->opts->mode) {
|
|
1107
|
+
case CompatMode: nd = WordNan; break;
|
|
1108
|
+
case StrictMode: nd = RaiseNan; break;
|
|
1109
|
+
case NullMode: nd = NullNan; break;
|
|
1110
|
+
default: break;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
switch (nd) {
|
|
1114
|
+
case RaiseNan: raise_strict(obj); break;
|
|
1115
|
+
case WordNan:
|
|
1116
|
+
strcpy(buf, "-Infinity");
|
|
1117
|
+
cnt = 9;
|
|
1118
|
+
break;
|
|
1119
|
+
case NullNan:
|
|
1120
|
+
strcpy(buf, "null");
|
|
1121
|
+
cnt = 4;
|
|
1122
|
+
break;
|
|
1123
|
+
case HugeNan:
|
|
1124
|
+
default:
|
|
1125
|
+
strcpy(buf, ninf_val);
|
|
1126
|
+
cnt = sizeof(ninf_val) - 1;
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
} else if (isnan(d)) {
|
|
1131
|
+
if (ObjectMode == out->opts->mode) {
|
|
1132
|
+
strcpy(buf, nan_val);
|
|
1133
|
+
cnt = sizeof(ninf_val) - 1;
|
|
1134
|
+
} else {
|
|
1135
|
+
NanDump nd = out->opts->dump_opts.nan_dump;
|
|
1136
|
+
|
|
1137
|
+
if (AutoNan == nd) {
|
|
1138
|
+
switch (out->opts->mode) {
|
|
1139
|
+
case ObjectMode: nd = HugeNan; break;
|
|
1140
|
+
case StrictMode: nd = RaiseNan; break;
|
|
1141
|
+
case NullMode: nd = NullNan; break;
|
|
1142
|
+
default: break;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
switch (nd) {
|
|
1146
|
+
case RaiseNan: raise_strict(obj); break;
|
|
1147
|
+
case WordNan:
|
|
1148
|
+
strcpy(buf, "NaN");
|
|
1149
|
+
cnt = 3;
|
|
1150
|
+
break;
|
|
1151
|
+
case NullNan:
|
|
1152
|
+
strcpy(buf, "null");
|
|
1153
|
+
cnt = 4;
|
|
1154
|
+
break;
|
|
1155
|
+
case HugeNan:
|
|
1156
|
+
default:
|
|
1157
|
+
strcpy(buf, nan_val);
|
|
1158
|
+
cnt = sizeof(nan_val) - 1;
|
|
1159
|
+
break;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
} else if (d == (double)(long long int)d) {
|
|
1163
|
+
cnt = snprintf(buf, sizeof(buf), "%.1f", d);
|
|
1164
|
+
} else if (0 == out->opts->float_prec) {
|
|
1165
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
1166
|
+
|
|
1167
|
+
cnt = (int)RSTRING_LEN(rstr);
|
|
1168
|
+
if ((int)sizeof(buf) <= cnt) {
|
|
1169
|
+
cnt = sizeof(buf) - 1;
|
|
1170
|
+
}
|
|
1171
|
+
memcpy(buf, RSTRING_PTR(rstr), cnt);
|
|
1172
|
+
buf[cnt] = '\0';
|
|
2713
1173
|
} 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
|
-
}
|
|
1174
|
+
cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
|
|
2729
1175
|
}
|
|
2730
|
-
|
|
1176
|
+
assure_size(out, cnt);
|
|
1177
|
+
APPEND_CHARS(out->cur, buf, cnt);
|
|
1178
|
+
*out->cur = '\0';
|
|
2731
1179
|
}
|
|
2732
1180
|
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
long size;
|
|
2736
|
-
DumpType type = sw->types[sw->depth];
|
|
1181
|
+
int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
|
|
1182
|
+
int cnt = snprintf(buf, blen, format, d);
|
|
2737
1183
|
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
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';
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
1184
|
+
// Round off issues at 16 significant digits so check for obvious ones of
|
|
1185
|
+
// 0001 and 9999.
|
|
1186
|
+
if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
|
|
1187
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
2765
1188
|
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
while (0 < sw->depth) {
|
|
2769
|
-
oj_str_writer_pop(sw);
|
|
1189
|
+
strcpy(buf, RSTRING_PTR(rstr));
|
|
1190
|
+
cnt = (int)RSTRING_LEN(rstr);
|
|
2770
1191
|
}
|
|
1192
|
+
return cnt;
|
|
2771
1193
|
}
|