oj 2.18.5 → 3.16.11

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