oj 2.18.3 → 3.13.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }