oj 2.18.3 → 3.13.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1324 -0
  3. data/README.md +51 -204
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +49 -72
  6. data/ext/oj/cache.c +326 -0
  7. data/ext/oj/cache.h +21 -0
  8. data/ext/oj/cache8.c +61 -64
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +37 -68
  11. data/ext/oj/circarray.h +16 -42
  12. data/ext/oj/code.c +221 -0
  13. data/ext/oj/code.h +40 -0
  14. data/ext/oj/compat.c +231 -107
  15. data/ext/oj/custom.c +1125 -0
  16. data/ext/oj/debug.c +132 -0
  17. data/ext/oj/dump.c +935 -2513
  18. data/ext/oj/dump.h +108 -0
  19. data/ext/oj/dump_compat.c +936 -0
  20. data/ext/oj/dump_leaf.c +164 -0
  21. data/ext/oj/dump_object.c +761 -0
  22. data/ext/oj/dump_strict.c +410 -0
  23. data/ext/oj/encode.h +7 -42
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +40 -54
  26. data/ext/oj/err.h +52 -46
  27. data/ext/oj/extconf.rb +21 -30
  28. data/ext/oj/fast.c +1097 -1080
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +893 -0
  32. data/ext/oj/object.c +549 -620
  33. data/ext/oj/odd.c +155 -167
  34. data/ext/oj/odd.h +37 -63
  35. data/ext/oj/oj.c +1661 -2063
  36. data/ext/oj/oj.h +341 -270
  37. data/ext/oj/parse.c +974 -737
  38. data/ext/oj/parse.h +105 -97
  39. data/ext/oj/parser.c +1526 -0
  40. data/ext/oj/parser.h +90 -0
  41. data/ext/oj/rails.c +1504 -0
  42. data/ext/oj/rails.h +18 -0
  43. data/ext/oj/reader.c +141 -163
  44. data/ext/oj/reader.h +75 -113
  45. data/ext/oj/resolve.c +45 -93
  46. data/ext/oj/resolve.h +7 -34
  47. data/ext/oj/rxclass.c +143 -0
  48. data/ext/oj/rxclass.h +26 -0
  49. data/ext/oj/saj.c +447 -511
  50. data/ext/oj/saj2.c +348 -0
  51. data/ext/oj/scp.c +91 -138
  52. data/ext/oj/sparse.c +793 -644
  53. data/ext/oj/stream_writer.c +331 -0
  54. data/ext/oj/strict.c +145 -109
  55. data/ext/oj/string_writer.c +493 -0
  56. data/ext/oj/trace.c +72 -0
  57. data/ext/oj/trace.h +28 -0
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +62 -70
  62. data/ext/oj/val_stack.h +95 -129
  63. data/ext/oj/validate.c +51 -0
  64. data/ext/oj/wab.c +622 -0
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +17 -8
  67. data/lib/oj/error.rb +10 -11
  68. data/lib/oj/json.rb +176 -0
  69. data/lib/oj/mimic.rb +158 -19
  70. data/lib/oj/state.rb +132 -0
  71. data/lib/oj/version.rb +2 -2
  72. data/lib/oj.rb +1 -31
  73. data/pages/Advanced.md +22 -0
  74. data/pages/Compatibility.md +25 -0
  75. data/pages/Custom.md +23 -0
  76. data/pages/Encoding.md +65 -0
  77. data/pages/JsonGem.md +94 -0
  78. data/pages/Modes.md +161 -0
  79. data/pages/Options.md +327 -0
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +167 -0
  82. data/pages/Security.md +20 -0
  83. data/pages/WAB.md +13 -0
  84. data/test/activerecord/result_test.rb +32 -0
  85. data/test/activesupport4/decoding_test.rb +108 -0
  86. data/test/activesupport4/encoding_test.rb +531 -0
  87. data/test/activesupport4/test_helper.rb +41 -0
  88. data/test/activesupport5/abstract_unit.rb +45 -0
  89. data/test/activesupport5/decoding_test.rb +133 -0
  90. data/test/activesupport5/encoding_test.rb +500 -0
  91. data/test/activesupport5/encoding_test_cases.rb +98 -0
  92. data/test/activesupport5/test_helper.rb +72 -0
  93. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  94. data/test/activesupport6/abstract_unit.rb +44 -0
  95. data/test/activesupport6/decoding_test.rb +133 -0
  96. data/test/activesupport6/encoding_test.rb +507 -0
  97. data/test/activesupport6/encoding_test_cases.rb +98 -0
  98. data/test/activesupport6/test_common.rb +17 -0
  99. data/test/activesupport6/test_helper.rb +163 -0
  100. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  101. data/test/activesupport7/abstract_unit.rb +49 -0
  102. data/test/activesupport7/decoding_test.rb +125 -0
  103. data/test/activesupport7/encoding_test.rb +486 -0
  104. data/test/activesupport7/encoding_test_cases.rb +104 -0
  105. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  106. data/test/bar.rb +9 -0
  107. data/test/baz.rb +16 -0
  108. data/test/bug.rb +11 -46
  109. data/test/foo.rb +69 -16
  110. data/test/helper.rb +10 -1
  111. data/test/isolated/shared.rb +12 -8
  112. data/test/isolated/test_mimic_rails_after.rb +3 -3
  113. data/test/isolated/test_mimic_rails_before.rb +3 -3
  114. data/test/json_gem/json_addition_test.rb +216 -0
  115. data/test/json_gem/json_common_interface_test.rb +153 -0
  116. data/test/json_gem/json_encoding_test.rb +107 -0
  117. data/test/json_gem/json_ext_parser_test.rb +20 -0
  118. data/test/json_gem/json_fixtures_test.rb +35 -0
  119. data/test/json_gem/json_generator_test.rb +397 -0
  120. data/test/json_gem/json_generic_object_test.rb +90 -0
  121. data/test/json_gem/json_parser_test.rb +470 -0
  122. data/test/json_gem/json_string_matching_test.rb +42 -0
  123. data/test/json_gem/test_helper.rb +26 -0
  124. data/test/mem.rb +33 -0
  125. data/test/perf.rb +1 -1
  126. data/test/perf_compat.rb +30 -28
  127. data/test/perf_dump.rb +50 -0
  128. data/test/perf_object.rb +1 -1
  129. data/test/perf_once.rb +58 -0
  130. data/test/perf_parser.rb +189 -0
  131. data/test/perf_scp.rb +11 -10
  132. data/test/perf_strict.rb +30 -19
  133. data/test/perf_wab.rb +131 -0
  134. data/test/prec.rb +23 -0
  135. data/test/sample.rb +0 -1
  136. data/test/sample_json.rb +1 -1
  137. data/test/test_compat.rb +219 -102
  138. data/test/test_custom.rb +533 -0
  139. data/test/test_fast.rb +107 -35
  140. data/test/test_file.rb +19 -25
  141. data/test/test_generate.rb +21 -0
  142. data/test/test_hash.rb +11 -1
  143. data/test/test_integer_range.rb +72 -0
  144. data/test/test_null.rb +376 -0
  145. data/test/test_object.rb +357 -70
  146. data/test/test_parser.rb +27 -0
  147. data/test/test_parser_saj.rb +245 -0
  148. data/test/test_parser_usual.rb +217 -0
  149. data/test/test_rails.rb +35 -0
  150. data/test/test_saj.rb +1 -1
  151. data/test/test_scp.rb +39 -2
  152. data/test/test_strict.rb +186 -7
  153. data/test/test_various.rb +160 -774
  154. data/test/test_wab.rb +307 -0
  155. data/test/test_writer.rb +90 -2
  156. data/test/tests.rb +24 -0
  157. data/test/tests_mimic.rb +14 -0
  158. data/test/tests_mimic_addition.rb +7 -0
  159. data/test/zoo.rb +13 -0
  160. metadata +194 -56
  161. data/ext/oj/hash.c +0 -163
  162. data/ext/oj/hash.h +0 -46
  163. data/ext/oj/hash_test.c +0 -512
  164. data/test/activesupport_datetime_test.rb +0 -23
  165. data/test/bug2.rb +0 -10
  166. data/test/bug3.rb +0 -46
  167. data/test/bug_fast.rb +0 -32
  168. data/test/bug_load.rb +0 -24
  169. data/test/crash.rb +0 -111
  170. data/test/curl/curl_oj.rb +0 -46
  171. data/test/curl/get_oj.rb +0 -24
  172. data/test/curl/just_curl.rb +0 -31
  173. data/test/curl/just_oj.rb +0 -51
  174. data/test/example.rb +0 -11
  175. data/test/io.rb +0 -48
  176. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  177. data/test/mod.rb +0 -16
  178. data/test/rails.rb +0 -50
  179. data/test/russian.rb +0 -18
  180. data/test/struct.rb +0 -29
  181. data/test/test_serializer.rb +0 -59
  182. data/test/write_timebars.rb +0 -31
data/ext/oj/dump.c CHANGED
@@ -1,118 +1,44 @@
1
- /* dump.c
2
- * Copyright (c) 2012, Peter Ohler
3
- * All rights reserved.
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
- #if !IS_WINDOWS
34
- #include <sys/time.h>
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
- #include <errno.h>
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
- #if !HAS_ENCODING_SUPPORT || defined(RUBINIUS_RUBY)
48
- #define rb_eEncodingError rb_eException
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
- typedef unsigned long ulong;
60
-
61
- static void raise_strict(VALUE obj);
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
- static void grow(Out out, size_t len);
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 void dump_leaf(Leaf leaf, int depth, Out out);
106
- static void dump_leaf_str(Leaf leaf, Out out);
107
- static void dump_leaf_fixnum(Leaf leaf, Out out);
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 hex_chars[17] = "0123456789abcdef";
38
+ static const char hex_chars[17] = "0123456789abcdef";
113
39
 
114
40
  // JSON standard except newlines are no escaped
115
- static char newline_friendly_chars[256] = "\
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 hibit_friendly_chars[256] = "\
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 ascii_friendly_chars[256] = "\
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 xss_friendly_chars[256] = "\
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
- inline static size_t
160
- newline_friendly_size(const uint8_t *str, size_t len) {
161
- size_t size = 0;
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
- for (; 0 < len; str++, len--) {
164
- size += newline_friendly_chars[*str];
165
- }
166
- return size - len * (size_t)'0';
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
- inline static size_t
170
- hibit_friendly_size(const uint8_t *str, size_t len) {
171
- size_t size = 0;
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
- for (; 0 < len; str++, len--) {
174
- size += hibit_friendly_chars[*str];
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
- ascii_friendly_size(const uint8_t *str, size_t len) {
181
- size_t size = 0;
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 (; 0 < len; str++, len--) {
184
- size += ascii_friendly_chars[*str];
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
- return size - len * (size_t)'0';
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 void
200
- fill_indent(Out out, int cnt) {
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 void
256
- dump_hex(uint8_t c, Out out) {
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 void
265
- dump_raw(const char *str, size_t cnt, Out out) {
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 char*
275
- dump_unicode(const char *str, const char *end, Out out) {
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 void
356
- dump_nil(Out out) {
357
- size_t size = 4;
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
- if (out->end - out->cur <= (long)size) {
360
- grow(out, size);
159
+ for (; 0 < i; str++, i--) {
160
+ size += hixss_friendly_chars[*str];
161
+ if (0 != (0x80 & *str)) {
162
+ check = true;
163
+ }
361
164
  }
362
- *out->cur++ = 'n';
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 void
370
- dump_true(Out out) {
371
- size_t size = 4;
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
- if (out->end - out->cur <= (long)size) {
374
- grow(out, size);
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
- for (; '\0' != *b; b++) {
426
- *out->cur++ = *b;
177
+ if (0 == hi) {
178
+ return size - len * (size_t)'0';
427
179
  }
428
- *out->cur = '\0';
180
+ return -(size - len * (size_t)'0');
429
181
  }
430
182
 
431
- static void
432
- dump_bignum(VALUE obj, Out out) {
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
- static const char inf_val[] = INF_VAL;
445
- static const char ninf_val[] = NINF_VAL;
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 (0.0 == d) {
457
- b = buf;
458
- *b++ = '0';
459
- *b++ = '.';
460
- *b++ = '0';
461
- *b++ = '\0';
462
- cnt = 3;
463
- } else if (OJ_INFINITY == d) {
464
- if (ObjectMode == out->opts->mode) {
465
- strcpy(buf, inf_val);
466
- cnt = sizeof(inf_val) - 1;
467
- } else {
468
- NanDump nd = out->opts->dump_opts.nan_dump;
469
-
470
- if (AutoNan == nd) {
471
- switch (out->opts->mode) {
472
- case CompatMode: nd = WordNan; break;
473
- case StrictMode: nd = RaiseNan; break;
474
- case NullMode: nd = NullNan; break;
475
- default: break;
476
- }
477
- }
478
- switch (nd) {
479
- case RaiseNan:
480
- raise_strict(obj);
481
- break;
482
- case WordNan:
483
- strcpy(buf, "Infinity");
484
- cnt = 8;
485
- break;
486
- case NullNan:
487
- strcpy(buf, "null");
488
- cnt = 4;
489
- break;
490
- case HugeNan:
491
- default:
492
- strcpy(buf, inf_val);
493
- cnt = sizeof(inf_val) - 1;
494
- break;
495
- }
496
- }
497
- } else if (-OJ_INFINITY == d) {
498
- if (ObjectMode == out->opts->mode) {
499
- strcpy(buf, ninf_val);
500
- cnt = sizeof(ninf_val) - 1;
501
- } else {
502
- NanDump nd = out->opts->dump_opts.nan_dump;
503
-
504
- if (AutoNan == nd) {
505
- switch (out->opts->mode) {
506
- case CompatMode: nd = WordNan; break;
507
- case StrictMode: nd = RaiseNan; break;
508
- case NullMode: nd = NullNan; break;
509
- default: break;
510
- }
511
- }
512
- switch (nd) {
513
- case RaiseNan:
514
- raise_strict(obj);
515
- break;
516
- case WordNan:
517
- strcpy(buf, "-Infinity");
518
- cnt = 9;
519
- break;
520
- case NullNan:
521
- strcpy(buf, "null");
522
- cnt = 4;
523
- break;
524
- case HugeNan:
525
- default:
526
- strcpy(buf, ninf_val);
527
- cnt = sizeof(ninf_val) - 1;
528
- break;
529
- }
530
- }
531
- } else if (isnan(d)) {
532
- if (ObjectMode == out->opts->mode) {
533
- strcpy(buf, nan_val);
534
- cnt = sizeof(nan_val) - 1;
535
- } else {
536
- NanDump nd = out->opts->dump_opts.nan_dump;
537
-
538
- if (AutoNan == nd) {
539
- switch (out->opts->mode) {
540
- case CompatMode: nd = WordNan; break;
541
- case StrictMode: nd = RaiseNan; break;
542
- case NullMode: nd = NullNan; break;
543
- default: break;
544
- }
545
- }
546
- switch (nd) {
547
- case RaiseNan:
548
- raise_strict(obj);
549
- break;
550
- case WordNan:
551
- strcpy(buf, "NaN");
552
- cnt = 3;
553
- break;
554
- case NullNan:
555
- strcpy(buf, "null");
556
- cnt = 4;
557
- break;
558
- case HugeNan:
559
- default:
560
- strcpy(buf, nan_val);
561
- cnt = sizeof(nan_val) - 1;
562
- break;
563
- }
564
- }
565
- } else if (d == (double)(long long int)d) {
566
- cnt = snprintf(buf, sizeof(buf), "%.1f", d);
567
- } else if (0 == out->opts->float_prec) {
568
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
569
-
570
- cnt = 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
- for (b = buf; '\0' != *b; b++) {
583
- *out->cur++ = *b;
584
- }
585
- *out->cur = '\0';
586
- }
587
-
588
- static void
589
- dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
590
- size_t size;
591
- char *cmap;
592
-
593
- switch (out->opts->escape_mode) {
594
- case NLEsc:
595
- cmap = newline_friendly_chars;
596
- size = newline_friendly_size((uint8_t*)str, cnt);
597
- break;
598
- case ASCIIEsc:
599
- cmap = ascii_friendly_chars;
600
- size = ascii_friendly_size((uint8_t*)str, cnt);
601
- break;
602
- case XSSEsc:
603
- cmap = xss_friendly_chars;
604
- size = xss_friendly_size((uint8_t*)str, cnt);
605
- break;
606
- case JSONEsc:
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
- cmap = hibit_friendly_chars;
609
- size = hibit_friendly_size((uint8_t*)str, cnt);
610
- }
611
- if (out->end - out->cur <= (long)size + BUFFER_EXTRA) { // extra 10 for escaped first char, quotes, and sym
612
- grow(out, size + BUFFER_EXTRA);
613
- }
614
- *out->cur++ = '"';
615
- if (escape1) {
616
- *out->cur++ = '\\';
617
- *out->cur++ = 'u';
618
- *out->cur++ = '0';
619
- *out->cur++ = '0';
620
- dump_hex((uint8_t)*str, out);
621
- cnt--;
622
- size--;
623
- str++;
624
- is_sym = 0; // just to make sure
625
- }
626
- if (cnt == size) {
627
- if (is_sym) {
628
- *out->cur++ = ':';
629
- }
630
- for (; '\0' != *str; str++) {
631
- *out->cur++ = *str;
632
- }
633
- *out->cur++ = '"';
634
- } else {
635
- const char *end = str + cnt;
636
-
637
- if (is_sym) {
638
- *out->cur++ = ':';
639
- }
640
- for (; str < end; str++) {
641
- switch (cmap[(uint8_t)*str]) {
642
- case '1':
643
- *out->cur++ = *str;
644
- break;
645
- case '2':
646
- *out->cur++ = '\\';
647
- switch (*str) {
648
- case '\\': *out->cur++ = '\\'; break;
649
- case '\b': *out->cur++ = 'b'; break;
650
- case '\t': *out->cur++ = 't'; break;
651
- case '\n': *out->cur++ = 'n'; break;
652
- case '\f': *out->cur++ = 'f'; break;
653
- case '\r': *out->cur++ = 'r'; break;
654
- default: *out->cur++ = *str; break;
655
- }
656
- break;
657
- case '3': // Unicode
658
- str = dump_unicode(str, end, out);
659
- break;
660
- case '6': // control characters
661
- *out->cur++ = '\\';
662
- *out->cur++ = 'u';
663
- *out->cur++ = '0';
664
- *out->cur++ = '0';
665
- dump_hex((uint8_t)*str, out);
666
- break;
667
- default:
668
- break; // ignore, should never happen if the table is correct
669
- }
670
- }
671
- *out->cur++ = '"';
672
- }
673
- *out->cur = '\0';
674
- }
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
- dump_sym_comp(VALUE obj, Out out) {
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
- static void
702
- dump_sym_obj(VALUE obj, Out out) {
703
- const char *sym = rb_id2name(SYM2ID(obj));
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
- dump_class_obj(VALUE obj, Out out) {
718
- const char *s = rb_class2name(obj);
719
- size_t len = strlen(s);
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
- if (out->end - out->cur <= 6) {
722
- grow(out, 6);
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
- *out->cur++ = '{';
725
- *out->cur++ = '"';
726
- *out->cur++ = '^';
727
- *out->cur++ = 'c';
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 void
736
- dump_array(VALUE a, VALUE clas, int depth, Out out) {
737
- size_t size;
738
- int i, cnt;
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 (id < 0) {
743
- return;
744
- }
745
- if (Qundef != clas && rb_cArray != clas && ObjectMode == out->opts->mode) {
746
- dump_obj_attrs(a, clas, 0, depth, out);
747
- return;
748
- }
749
- cnt = (int)RARRAY_LEN(a);
750
- *out->cur++ = '[';
751
- if (0 < id) {
752
- size = d2 * out->indent + 16;
753
- if (out->end - out->cur <= (long)size) {
754
- grow(out, size);
755
- }
756
- fill_indent(out, d2);
757
- *out->cur++ = '"';
758
- *out->cur++ = '^';
759
- *out->cur++ = 'i';
760
- dump_ulong(id, out);
761
- *out->cur++ = '"';
762
- }
763
- size = 2;
764
- if (out->end - out->cur <= (long)size) {
765
- grow(out, size);
766
- }
767
- if (0 == cnt) {
768
- *out->cur++ = ']';
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
- if (0 < id) {
771
- *out->cur++ = ',';
772
- }
773
- if (out->opts->dump_opts.use) {
774
- size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
775
- } else {
776
- size = d2 * out->indent + 2;
777
- }
778
- cnt--;
779
- for (i = 0; i <= cnt; i++) {
780
- if (out->end - out->cur <= (long)size) {
781
- grow(out, size);
782
- }
783
- if (out->opts->dump_opts.use) {
784
- if (0 < out->opts->dump_opts.array_size) {
785
- strcpy(out->cur, out->opts->dump_opts.array_nl);
786
- out->cur += out->opts->dump_opts.array_size;
787
- }
788
- if (0 < out->opts->dump_opts.indent_size) {
789
- int i;
790
- for (i = d2; 0 < i; i--) {
791
- strcpy(out->cur, out->opts->dump_opts.indent_str);
792
- out->cur += out->opts->dump_opts.indent_size;
793
- }
794
- }
795
- } else {
796
- fill_indent(out, d2);
797
- }
798
- dump_val(rb_ary_entry(a, i), d2, out, 0, 0, true);
799
- if (i < cnt) {
800
- *out->cur++ = ',';
801
- }
802
- }
803
- size = depth * out->indent + 1;
804
- if (out->end - out->cur <= (long)size) {
805
- grow(out, size);
806
- }
807
- if (out->opts->dump_opts.use) {
808
- //printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
809
- if (0 < out->opts->dump_opts.array_size) {
810
- strcpy(out->cur, out->opts->dump_opts.array_nl);
811
- out->cur += out->opts->dump_opts.array_size;
812
- }
813
- if (0 < out->opts->dump_opts.indent_size) {
814
- int i;
815
-
816
- for (i = depth; 0 < i; i--) {
817
- strcpy(out->cur, out->opts->dump_opts.indent_str);
818
- out->cur += out->opts->dump_opts.indent_size;
819
- }
820
- }
821
- } else {
822
- fill_indent(out, depth);
823
- }
824
- *out->cur++ = ']';
277
+ cnt = 0;
278
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
825
279
  }
826
- *out->cur = '\0';
827
- }
828
-
829
- static int
830
- hash_cb_strict(VALUE key, VALUE value, Out out) {
831
- int depth = out->depth;
832
- long size;
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
- dump_val(value, depth, out, 0, 0, false);
889
- out->depth = depth;
890
- *out->cur++ = ',';
891
-
892
- return ST_CONTINUE;
893
- }
894
-
895
- static int
896
- hash_cb_compat(VALUE key, VALUE value, Out out) {
897
- int depth = out->depth;
898
- long size;
288
+ if (0x0000FFFF < code) {
289
+ uint32_t c1;
899
290
 
900
- if (out->omit_nil && Qnil == value) {
901
- return ST_CONTINUE;
902
- }
903
- if (!out->opts->dump_opts.use) {
904
- size = depth * out->indent + 1;
905
- if (out->end - out->cur <= size) {
906
- grow(out, size);
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
- switch (rb_type(key)) {
927
- case T_STRING:
928
- dump_str_comp(key, out);
929
- break;
930
- case T_SYMBOL:
931
- dump_sym_comp(key, out);
932
- break;
933
- default:
934
- /*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
935
- dump_str_comp(rb_funcall(key, oj_to_s_id, 0), out);
936
- break;
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
- dump_val(value, depth, out, 0, 0, true);
956
- out->depth = depth;
957
- *out->cur++ = ',';
958
-
959
- return ST_CONTINUE;
303
+ return str - 1;
960
304
  }
961
305
 
962
- static int
963
- hash_cb_object(VALUE key, VALUE value, Out out) {
964
- int depth = out->depth;
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 (out->omit_nil && Qnil == value) {
968
- return ST_CONTINUE;
969
- }
970
- if (out->end - out->cur <= size) {
971
- grow(out, size);
972
- }
973
- fill_indent(out, depth);
974
- if (rb_type(key) == T_STRING) {
975
- dump_str_obj(key, Qundef, depth, out);
976
- *out->cur++ = ':';
977
- dump_val(value, depth, out, 0, 0, true);
978
- } else if (rb_type(key) == T_SYMBOL) {
979
- dump_sym_obj(key, out);
980
- *out->cur++ = ':';
981
- dump_val(value, depth, out, 0, 0, true);
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
- int d2 = depth + 1;
984
- long s2 = size + out->indent + 1;
985
- int i;
986
- int started = 0;
987
- uint8_t b;
988
-
989
- if (out->end - out->cur <= s2 + 15) {
990
- grow(out, s2 + 15);
991
- }
992
- *out->cur++ = '"';
993
- *out->cur++ = '^';
994
- *out->cur++ = '#';
995
- out->hash_cnt++;
996
- for (i = 28; 0 <= i; i -= 4) {
997
- b = (uint8_t)((out->hash_cnt >> i) & 0x0000000F);
998
- if ('\0' != b) {
999
- started = 1;
1000
- }
1001
- if (started) {
1002
- *out->cur++ = hex_chars[b];
1003
- }
1004
- }
1005
- *out->cur++ = '"';
1006
- *out->cur++ = ':';
1007
- *out->cur++ = '[';
1008
- fill_indent(out, d2);
1009
- dump_val(key, d2, out, 0, 0, true);
1010
- if (out->end - out->cur <= (long)s2) {
1011
- grow(out, s2);
1012
- }
1013
- *out->cur++ = ',';
1014
- fill_indent(out, d2);
1015
- dump_val(value, d2, out, 0, 0, true);
1016
- if (out->end - out->cur <= (long)size) {
1017
- grow(out, size);
1018
- }
1019
- fill_indent(out, depth);
1020
- *out->cur++ = ']';
321
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
1021
322
  }
1022
- out->depth = depth;
1023
- *out->cur++ = ',';
1024
-
1025
- return ST_CONTINUE;
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
- static void
1029
- dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out) {
1030
- int cnt;
1031
- size_t size;
1032
-
1033
- if (Qundef != clas && rb_cHash != clas && ObjectMode == mode) {
1034
- dump_obj_attrs(obj, clas, 0, depth, out);
1035
- return;
1036
- }
1037
- cnt = (int)RHASH_SIZE(obj);
1038
- size = depth * out->indent + 2;
1039
- if (out->end - out->cur <= 2) {
1040
- grow(out, 2);
1041
- }
1042
- if (0 == cnt) {
1043
- *out->cur++ = '{';
1044
- *out->cur++ = '}';
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
- long id = check_circular(obj, out);
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
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
1118
- #if HAS_NANO_TIME
1119
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
1120
- #else
1121
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
1122
- #endif
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
- long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
1128
- int zneg = (0 > tzsecs);
1129
-
1130
- if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
1131
- tzsecs = 86400;
1132
- }
1133
- if (zneg) {
1134
- tzsecs = -tzsecs;
1135
- }
1136
- if (0 == tzsecs) {
1137
- *b-- = '0';
1138
- } else {
1139
- for (; 0 < tzsecs; b--, tzsecs /= 10) {
1140
- *b = '0' + (tzsecs % 10);
1141
- }
1142
- if (zneg) {
1143
- *b-- = '-';
1144
- }
1145
- }
1146
- *b-- = 'e';
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
- neg = 1;
1150
- sec = -sec;
1151
- if (0 < nsec) {
1152
- nsec = 1000000000 - nsec;
1153
- sec--;
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
- if (9 > out->opts->sec_prec) {
1159
- int i;
1160
-
1161
- for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
1162
- dot++;
1163
- nsec = (nsec + 5) / 10;
1164
- one /= 10;
1165
- }
1166
- }
1167
- if (one <= nsec) {
1168
- nsec -= one;
1169
- sec++;
1170
- }
1171
- for (; dot < b; b--, nsec /= 10) {
1172
- *b = '0' + (nsec % 10);
1173
- }
1174
- *b-- = '.';
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
- *b-- = '0';
438
+ *b-- = '0';
1178
439
  } else {
1179
- for (; 0 < sec; b--, sec /= 10) {
1180
- *b = '0' + (sec % 10);
1181
- }
440
+ for (; 0 < sec; b--, sec /= 10) {
441
+ *b = '0' + (sec % 10);
442
+ }
1182
443
  }
1183
444
  if (neg) {
1184
- *b-- = '-';
445
+ *b-- = '-';
1185
446
  }
1186
447
  b++;
1187
448
  size = sizeof(buf) - (b - buf) - 1;
1188
- if (out->end - out->cur <= size) {
1189
- grow(out, size);
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
- static void
1197
- dump_ruby_time(VALUE obj, Out out) {
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
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
457
+ oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1201
458
  }
1202
459
 
1203
- static void
1204
- dump_xml_time(VALUE obj, Out out) {
1205
- char buf[64];
1206
- struct tm *tm;
1207
- long one = 1000000000;
1208
- #if HAS_RB_TIME_TIMESPEC
1209
- struct timespec ts = rb_time_timespec(obj);
1210
- time_t sec = ts.tv_sec;
1211
- long nsec = ts.tv_nsec;
1212
- #else
1213
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
1214
- #if HAS_NANO_TIME
1215
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
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
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
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
- if (out->end - out->cur <= 36) {
1225
- grow(out, 36);
1226
- }
485
+ assure_size(out, 36);
1227
486
  if (9 > out->opts->sec_prec) {
1228
- int i;
1229
-
1230
- for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
1231
- nsec = (nsec + 5) / 10;
1232
- one /= 10;
1233
- }
1234
- if (one <= nsec) {
1235
- nsec -= one;
1236
- sec++;
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
- tm = gmtime(&sec);
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 = (int)(tzsecs / -60) - (tzhour * 60);
513
+ tzmin = (int)(tzsecs / -60) - (tzhour * 60);
1248
514
  } else {
1249
515
  tzhour = (int)(tzsecs / 3600);
1250
- tzmin = (int)(tzsecs / 60) - (tzhour * 60);
1251
- }
1252
- #else
1253
- if (0 > tm->tm_gmtoff) {
1254
- tzsign = '-';
1255
- tzhour = (int)(tm->tm_gmtoff / -3600);
1256
- tzmin = (int)(tm->tm_gmtoff / -60) - (tzhour * 60);
1257
- } else {
1258
- tzhour = (int)(tm->tm_gmtoff / 3600);
1259
- tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
1260
- }
1261
- #endif
1262
- if (0 == nsec || 0 == out->opts->sec_prec) {
1263
- if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
1264
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
1265
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1266
- tm->tm_hour, tm->tm_min, tm->tm_sec);
1267
- dump_cstr(buf, 20, 0, 0, out);
1268
- } else {
1269
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
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
- char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
1277
- int len = 30;
1278
-
1279
- if (9 > out->opts->sec_prec) {
1280
- format[32] = '0' + out->opts->sec_prec;
1281
- len -= 9 - out->opts->sec_prec;
1282
- }
1283
- sprintf(buf, format,
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
- char buf[32];
1619
-
1620
- *buf = '~';
1621
- strncpy(buf + 1, attr, sizeof(buf) - 2);
1622
- buf[sizeof(buf) - 1] = '\0';
1623
- dump_cstr(buf, strlen(buf), 0, 0, out);
1624
- }
1625
- *out->cur++ = ':';
1626
- dump_val(value, depth, out, 0, 0, true);
1627
- out->depth = depth;
1628
- *out->cur++ = ',';
1629
-
1630
- return ST_CONTINUE;
1631
- }
1632
- #endif
1633
-
1634
- static void
1635
- dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1636
- size_t size = 0;
1637
- int d2 = depth + 1;
1638
- int type = rb_type(obj);
1639
-
1640
- if (out->end - out->cur <= 2) {
1641
- grow(out, 2);
1642
- }
1643
- *out->cur++ = '{';
1644
- if (Qundef != clas) {
1645
- const char *class_name = rb_class2name(clas);
1646
- int clen = (int)strlen(class_name);
1647
-
1648
- size = d2 * out->indent + clen + 10;
1649
- if (out->end - out->cur <= (long)size) {
1650
- grow(out, size);
1651
- }
1652
- fill_indent(out, d2);
1653
- *out->cur++ = '"';
1654
- *out->cur++ = '^';
1655
- *out->cur++ = 'o';
1656
- *out->cur++ = '"';
1657
- *out->cur++ = ':';
1658
- dump_cstr(class_name, clen, 0, 0, out);
1659
- }
1660
- if (0 < id) {
1661
- size = d2 * out->indent + 16;
1662
- if (out->end - out->cur <= (long)size) {
1663
- grow(out, size);
1664
- }
1665
- *out->cur++ = ',';
1666
- fill_indent(out, d2);
1667
- *out->cur++ = '"';
1668
- *out->cur++ = '^';
1669
- *out->cur++ = 'i';
1670
- *out->cur++ = '"';
1671
- *out->cur++ = ':';
1672
- dump_ulong(id, out);
1673
- }
1674
- switch (type) {
1675
- case T_STRING:
1676
- size = d2 * out->indent + 14;
1677
- if (out->end - out->cur <= (long)size) {
1678
- grow(out, size);
1679
- }
1680
- *out->cur++ = ',';
1681
- fill_indent(out, d2);
1682
- *out->cur++ = '"';
1683
- *out->cur++ = 's';
1684
- *out->cur++ = 'e';
1685
- *out->cur++ = 'l';
1686
- *out->cur++ = 'f';
1687
- *out->cur++ = '"';
1688
- *out->cur++ = ':';
1689
- dump_cstr(rb_string_value_ptr((VALUE*)&obj), 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
- if (MAX_DEPTH < depth) {
2118
- rb_raise(rb_eNoMemError, "Too deeply nested.\n");
2119
- }
2120
- #ifdef OJ_DEBUG
2121
- printf("Oj-debug: dump_val %s\n", rb_class2name(rb_obj_class(obj)));
2122
- #endif
2123
- switch (type) {
2124
- case T_NIL: dump_nil(out); break;
2125
- case T_TRUE: dump_true(out); break;
2126
- case T_FALSE: dump_false(out); break;
2127
- case T_FIXNUM: dump_fixnum(obj, out); break;
2128
- case T_FLOAT: dump_float(obj, out); break;
2129
- case T_MODULE:
2130
- case T_CLASS:
2131
- switch (out->opts->mode) {
2132
- case StrictMode: raise_strict(obj); break;
2133
- case NullMode: dump_nil(out); break;
2134
- case CompatMode: dump_class_comp(obj, out); break;
2135
- case ObjectMode:
2136
- default: dump_class_obj(obj, out); break;
2137
- }
2138
- break;
2139
- case T_SYMBOL:
2140
- switch (out->opts->mode) {
2141
- case StrictMode: raise_strict(obj); break;
2142
- case NullMode: dump_nil(out); break;
2143
- case CompatMode: dump_sym_comp(obj, out); break;
2144
- case ObjectMode:
2145
- default: dump_sym_obj(obj, out); break;
2146
- }
2147
- break;
2148
- case T_STRUCT: // for Range
2149
- switch (out->opts->mode) {
2150
- case StrictMode: raise_strict(obj); break;
2151
- case NullMode: dump_nil(out); break;
2152
- case CompatMode: dump_struct_comp(obj, depth, out, argc, argv, as_ok); break;
2153
- case ObjectMode:
2154
- default: dump_struct_obj(obj, depth, out); break;
2155
- }
2156
- break;
2157
- default:
2158
- // Most developers have enough sense not to subclass primitive types but
2159
- // since these classes could potentially be subclassed a check for odd
2160
- // classes is performed.
2161
- {
2162
- VALUE clas = rb_obj_class(obj);
2163
- Odd odd;
2164
-
2165
- if (ObjectMode == out->opts->mode && 0 != (odd = oj_get_odd(clas))) {
2166
- dump_odd(obj, odd, clas, depth + 1, out);
2167
- return;
2168
- }
2169
- switch (type) {
2170
- case T_BIGNUM: dump_bignum(obj, out); break;
2171
- case T_STRING:
2172
- switch (out->opts->mode) {
2173
- case StrictMode:
2174
- case NullMode:
2175
- case CompatMode: dump_str_comp(obj, out); break;
2176
- case ObjectMode:
2177
- default: dump_str_obj(obj, clas, depth, out); break;
2178
- }
2179
- break;
2180
- case T_ARRAY: dump_array(obj, clas, depth, out); break;
2181
- case T_HASH: dump_hash(obj, clas, depth, out->opts->mode, out); break;
2182
- #if (defined T_RATIONAL && defined RRATIONAL)
2183
- case T_RATIONAL:
2184
- #endif
2185
- case T_OBJECT:
2186
- switch (out->opts->mode) {
2187
- case StrictMode: dump_data_strict(obj, out); break;
2188
- case NullMode: dump_data_null(obj, out); break;
2189
- case CompatMode: dump_obj_comp(obj, depth, out, argc, argv, as_ok); break;
2190
- case ObjectMode:
2191
- default: dump_obj_obj(obj, depth, out); break;
2192
- }
2193
- break;
2194
- case T_DATA:
2195
- switch (out->opts->mode) {
2196
- case StrictMode: dump_data_strict(obj, out); break;
2197
- case NullMode: dump_data_null(obj, out); break;
2198
- case CompatMode: dump_data_comp(obj, depth, out, argc, argv, as_ok);break;
2199
- case ObjectMode:
2200
- default: dump_data_obj(obj, depth, out); break;
2201
- }
2202
- break;
2203
- #if (defined T_COMPLEX && defined RCOMPLEX)
2204
- case T_COMPLEX:
2205
- #endif
2206
- case T_REGEXP:
2207
- switch (out->opts->mode) {
2208
- case StrictMode: raise_strict(obj); break;
2209
- case NullMode: dump_nil(out); break;
2210
- case CompatMode:
2211
- case ObjectMode:
2212
- default: dump_obj_comp(obj, depth, out, argc, argv, as_ok); break;
2213
- }
2214
- break;
2215
- default:
2216
- switch (out->opts->mode) {
2217
- case StrictMode: raise_strict(obj); break;
2218
- case NullMode: dump_nil(out); break;
2219
- case CompatMode: {
2220
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
2221
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), 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
- out->buf = ALLOC_N(char, 4096);
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 = copts;
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
- oj_cache8_new(&out->circ_cache);
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
- switch (*(out->cur - 1)) {
2259
- case ']':
2260
- case '}':
2261
- grow(out, 1);
2262
- *out->cur++ = '\n';
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
- oj_cache8_delete(out->circ_cache);
594
+ oj_cache8_delete(out->circ_cache);
2270
595
  }
2271
596
  }
2272
597
 
2273
- void
2274
- oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
2275
- char buf[4096];
2276
- struct _Out out;
2277
- size_t size;
2278
- FILE *f;
2279
- int ok;
2280
-
2281
- out.buf = buf;
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
- if (out.allocated) {
2289
- xfree(out.buf);
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
- if (out.allocated) {
2295
- xfree(out.buf);
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
- if (!ok) {
2299
- int err = ferror(f);
624
+ }
2300
625
 
2301
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
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
- oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
2307
- char buf[4096];
2308
- struct _Out out;
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 fd;
2313
- VALUE s;
648
+ int fd;
649
+ VALUE s;
2314
650
  #endif
2315
651
 
2316
- out.buf = buf;
2317
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
2318
- out.allocated = 0;
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
- rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
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
- Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
2327
- 0 != (fd = FIX2INT(s))) {
2328
- if (size != write(fd, out.buf, size)) {
2329
- if (out.allocated) {
2330
- xfree(out.buf);
2331
- }
2332
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", errno, strerror(errno));
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
- rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
681
+ rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
2337
682
  } else {
2338
- if (out.allocated) {
2339
- xfree(out.buf);
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
- // dump leaf functions
689
+ void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
690
+ int idx = RB_ENCODING_GET(obj);
2349
691
 
2350
- inline static void
2351
- dump_chars(const char *s, size_t size, Out out) {
2352
- if (out->end - out->cur <= (long)size) {
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
- memcpy(out->cur, s, size);
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
- static void
2361
- dump_leaf_str(Leaf leaf, Out out) {
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
- static void
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
- dump_leaf_float(Leaf leaf, Out out) {
2398
- switch (leaf->value_type) {
2399
- case STR_VAL:
2400
- dump_chars(leaf->str, strlen(leaf->str), out);
2401
- break;
2402
- case RUBY_VAL:
2403
- dump_float(leaf->value, out);
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
- static void
2413
- dump_leaf_array(Leaf leaf, int depth, Out out) {
2414
- size_t size;
2415
- int d2 = depth + 1;
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
- size = 2;
2418
- if (out->end - out->cur <= (long)size) {
2419
- grow(out, size);
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
- Leaf first = leaf->elements->next;
2426
- Leaf e = first;
2427
-
2428
- size = d2 * out->indent + 2;
2429
- do {
2430
- if (out->end - out->cur <= (long)size) {
2431
- grow(out, size);
2432
- }
2433
- fill_indent(out, d2);
2434
- dump_leaf(e, d2, out);
2435
- if (e->next != first) {
2436
- *out->cur++ = ',';
2437
- }
2438
- e = e->next;
2439
- } while (e != first);
2440
- size = depth * out->indent + 1;
2441
- if (out->end - out->cur <= (long)size) {
2442
- grow(out, size);
2443
- }
2444
- fill_indent(out, depth);
2445
- *out->cur++ = ']';
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
- static void
2451
- dump_leaf_hash(Leaf leaf, int depth, Out out) {
2452
- size_t size;
2453
- int d2 = depth + 1;
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
- size = 2;
2456
- if (out->end - out->cur <= (long)size) {
2457
- grow(out, size);
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
- *out->cur++ = '{';
2460
- if (0 == leaf->elements) {
2461
- *out->cur++ = '}';
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
- Leaf first = leaf->elements->next;
2464
- Leaf e = first;
2465
-
2466
- size = d2 * out->indent + 2;
2467
- do {
2468
- if (out->end - out->cur <= (long)size) {
2469
- grow(out, size);
2470
- }
2471
- fill_indent(out, d2);
2472
- dump_cstr(e->key, strlen(e->key), 0, 0, out);
2473
- *out->cur++ = ':';
2474
- dump_leaf(e, d2, out);
2475
- if (e->next != first) {
2476
- *out->cur++ = ',';
2477
- }
2478
- e = e->next;
2479
- } while (e != first);
2480
- size = depth * out->indent + 1;
2481
- if (out->end - out->cur <= (long)size) {
2482
- grow(out, size);
2483
- }
2484
- fill_indent(out, depth);
2485
- *out->cur++ = '}';
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
- static void
2491
- dump_leaf(Leaf leaf, int depth, Out out) {
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
- void
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
- oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts) {
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
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
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
- // string writer functions
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
- static void
2568
- key_check(StrWriter sw, const char *key) {
2569
- DumpType type = sw->types[sw->depth];
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
- if (0 == key && (ObjectNew == type || ObjectType == type)) {
2572
- rb_raise(rb_eStandardError, "Can not push onto an Object without a key.");
942
+ void oj_out_free(Out out) {
943
+ if (out->allocated) {
944
+ xfree(out->buf); // TBD
2573
945
  }
2574
946
  }
2575
947
 
2576
- static void
2577
- push_type(StrWriter sw, DumpType type) {
2578
- if (sw->types_end <= sw->types + sw->depth + 1) {
2579
- size_t size = (sw->types_end - sw->types) * 2;
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
- REALLOC_N(sw->types, char, size);
2582
- sw->types_end = sw->types + size;
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
- sw->depth++;
2585
- sw->types[sw->depth] = type;
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
- static void
2589
- maybe_comma(StrWriter sw) {
2590
- switch (sw->types[sw->depth]) {
2591
- case ObjectNew:
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
- oj_str_writer_push_key(StrWriter sw, const char *key) {
2607
- DumpType type = sw->types[sw->depth];
2608
- long size;
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
- if (sw->keyWritten) {
2611
- rb_raise(rb_eStandardError, "Can not push more than one key before pushing a non-key.");
2612
- }
2613
- if (ObjectNew != type && ObjectType != type) {
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
- oj_str_writer_push_object(StrWriter sw, const char *key) {
2631
- if (sw->keyWritten) {
2632
- sw->keyWritten = 0;
2633
- if (sw->out.end - sw->out.cur <= 1) {
2634
- grow(&sw->out, 1);
2635
- }
2636
- } else {
2637
- long size;
2638
-
2639
- key_check(sw, key);
2640
- size = sw->depth * sw->out.indent + 3;
2641
- if (sw->out.end - sw->out.cur <= (long)size) {
2642
- grow(&sw->out, size);
2643
- }
2644
- maybe_comma(sw);
2645
- if (0 < sw->depth) {
2646
- fill_indent(&sw->out, sw->depth);
2647
- }
2648
- if (0 != key) {
2649
- dump_cstr(key, strlen(key), 0, 0, &sw->out);
2650
- *sw->out.cur++ = ':';
2651
- }
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
- *sw->out.cur++ = '{';
2654
- push_type(sw, ObjectNew);
2655
- }
1002
+ if (0 > num) {
1003
+ neg = 1;
1004
+ num = -num;
1005
+ }
1006
+ *b-- = '\0';
2656
1007
 
2657
- void
2658
- oj_str_writer_push_array(StrWriter sw, const char *key) {
2659
- if (sw->keyWritten) {
2660
- sw->keyWritten = 0;
2661
- if (sw->out.end - sw->out.cur <= 1) {
2662
- grow(&sw->out, 1);
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
- long size;
2666
-
2667
- key_check(sw, key);
2668
- size = sw->depth * sw->out.indent + 3;
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
- *sw->out.cur++ = '[';
2682
- push_type(sw, ArrayNew);
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
- oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
2687
- if (sw->keyWritten) {
2688
- sw->keyWritten = 0;
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
- long size;
2691
-
2692
- key_check(sw, key);
2693
- size = sw->depth * sw->out.indent + 3;
2694
- if (sw->out.end - sw->out.cur <= size) {
2695
- grow(&sw->out, size);
2696
- }
2697
- maybe_comma(sw);
2698
- if (0 < sw->depth) {
2699
- fill_indent(&sw->out, sw->depth);
2700
- }
2701
- if (0 != key) {
2702
- dump_cstr(key, strlen(key), 0, 0, &sw->out);
2703
- *sw->out.cur++ = ':';
2704
- }
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
- dump_val(val, sw->depth, &sw->out, 0, 0, true);
1048
+ *out->cur = '\0';
2707
1049
  }
2708
1050
 
2709
- void
2710
- oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
2711
- if (sw->keyWritten) {
2712
- sw->keyWritten = 0;
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
- long size;
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
- dump_raw(json, strlen(json), &sw->out);
1176
+ assure_size(out, cnt);
1177
+ APPEND_CHARS(out->cur, buf, cnt);
1178
+ *out->cur = '\0';
2731
1179
  }
2732
1180
 
2733
- void
2734
- oj_str_writer_pop(StrWriter sw) {
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
- if (sw->keyWritten) {
2739
- sw->keyWritten = 0;
2740
- rb_raise(rb_eStandardError, "Can not pop after writing a key but no value.");
2741
- }
2742
- sw->depth--;
2743
- if (0 > sw->depth) {
2744
- rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
2745
- }
2746
- size = sw->depth * sw->out.indent + 2;
2747
- if (sw->out.end - sw->out.cur <= size) {
2748
- grow(&sw->out, size);
2749
- }
2750
- fill_indent(&sw->out, sw->depth);
2751
- switch (type) {
2752
- case ObjectNew:
2753
- case ObjectType:
2754
- *sw->out.cur++ = '}';
2755
- break;
2756
- case ArrayNew:
2757
- case ArrayType:
2758
- *sw->out.cur++ = ']';
2759
- break;
2760
- }
2761
- if (0 == sw->depth && 0 <= sw->out.indent) {
2762
- *sw->out.cur++ = '\n';
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
- void
2767
- oj_str_writer_pop_all(StrWriter sw) {
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
  }