oj 3.11.0 → 3.16.5

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1421 -0
  3. data/README.md +20 -5
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +48 -38
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +60 -62
  9. data/ext/oj/cache8.h +8 -7
  10. data/ext/oj/circarray.c +35 -35
  11. data/ext/oj/circarray.h +11 -9
  12. data/ext/oj/code.c +156 -174
  13. data/ext/oj/code.h +19 -18
  14. data/ext/oj/compat.c +140 -197
  15. data/ext/oj/custom.c +737 -879
  16. data/ext/oj/debug.c +126 -0
  17. data/ext/oj/dump.c +830 -835
  18. data/ext/oj/dump.h +65 -53
  19. data/ext/oj/dump_compat.c +566 -642
  20. data/ext/oj/dump_leaf.c +95 -182
  21. data/ext/oj/dump_object.c +518 -659
  22. data/ext/oj/dump_strict.c +301 -334
  23. data/ext/oj/encode.h +3 -4
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +27 -24
  26. data/ext/oj/err.h +38 -13
  27. data/ext/oj/extconf.rb +23 -7
  28. data/ext/oj/fast.c +1043 -1073
  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 +449 -423
  34. data/ext/oj/object.c +530 -576
  35. data/ext/oj/odd.c +155 -138
  36. data/ext/oj/odd.h +24 -22
  37. data/ext/oj/oj.c +1331 -993
  38. data/ext/oj/oj.h +306 -292
  39. data/ext/oj/parse.c +934 -938
  40. data/ext/oj/parse.h +73 -70
  41. data/ext/oj/parser.c +1600 -0
  42. data/ext/oj/parser.h +101 -0
  43. data/ext/oj/rails.c +795 -845
  44. data/ext/oj/rails.h +7 -7
  45. data/ext/oj/reader.c +132 -140
  46. data/ext/oj/reader.h +67 -78
  47. data/ext/oj/resolve.c +40 -59
  48. data/ext/oj/resolve.h +3 -2
  49. data/ext/oj/rxclass.c +67 -67
  50. data/ext/oj/rxclass.h +11 -9
  51. data/ext/oj/saj.c +441 -480
  52. data/ext/oj/saj2.c +584 -0
  53. data/ext/oj/saj2.h +23 -0
  54. data/ext/oj/scp.c +78 -111
  55. data/ext/oj/sparse.c +726 -730
  56. data/ext/oj/stream_writer.c +146 -165
  57. data/ext/oj/strict.c +103 -123
  58. data/ext/oj/string_writer.c +241 -253
  59. data/ext/oj/trace.c +29 -33
  60. data/ext/oj/trace.h +41 -11
  61. data/ext/oj/usual.c +1218 -0
  62. data/ext/oj/usual.h +69 -0
  63. data/ext/oj/util.c +103 -103
  64. data/ext/oj/util.h +3 -2
  65. data/ext/oj/val_stack.c +60 -49
  66. data/ext/oj/val_stack.h +79 -85
  67. data/ext/oj/validate.c +46 -0
  68. data/ext/oj/wab.c +307 -350
  69. data/lib/oj/active_support_helper.rb +1 -3
  70. data/lib/oj/bag.rb +8 -1
  71. data/lib/oj/easy_hash.rb +9 -9
  72. data/lib/oj/error.rb +1 -2
  73. data/lib/oj/json.rb +162 -150
  74. data/lib/oj/mimic.rb +9 -19
  75. data/lib/oj/saj.rb +20 -6
  76. data/lib/oj/schandler.rb +5 -4
  77. data/lib/oj/state.rb +12 -8
  78. data/lib/oj/version.rb +1 -2
  79. data/lib/oj.rb +2 -0
  80. data/pages/Compatibility.md +1 -1
  81. data/pages/InstallOptions.md +20 -0
  82. data/pages/JsonGem.md +15 -0
  83. data/pages/Modes.md +8 -3
  84. data/pages/Options.md +43 -5
  85. data/pages/Parser.md +309 -0
  86. data/pages/Rails.md +14 -2
  87. data/test/_test_active.rb +8 -9
  88. data/test/_test_active_mimic.rb +7 -8
  89. data/test/_test_mimic_rails.rb +17 -20
  90. data/test/activerecord/result_test.rb +12 -8
  91. data/test/activesupport6/encoding_test.rb +63 -28
  92. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  93. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  94. data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
  95. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  96. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  97. data/test/files.rb +15 -15
  98. data/test/foo.rb +17 -43
  99. data/test/helper.rb +16 -3
  100. data/test/isolated/shared.rb +3 -2
  101. data/test/json_gem/json_addition_test.rb +2 -2
  102. data/test/json_gem/json_common_interface_test.rb +8 -6
  103. data/test/json_gem/json_encoding_test.rb +0 -0
  104. data/test/json_gem/json_ext_parser_test.rb +1 -0
  105. data/test/json_gem/json_fixtures_test.rb +3 -2
  106. data/test/json_gem/json_generator_test.rb +71 -41
  107. data/test/json_gem/json_generic_object_test.rb +11 -11
  108. data/test/json_gem/json_parser_test.rb +54 -47
  109. data/test/json_gem/json_string_matching_test.rb +9 -9
  110. data/test/json_gem/test_helper.rb +12 -0
  111. data/test/mem.rb +34 -0
  112. data/test/perf.rb +22 -27
  113. data/test/perf_compat.rb +31 -33
  114. data/test/perf_dump.rb +50 -0
  115. data/test/perf_fast.rb +80 -82
  116. data/test/perf_file.rb +27 -29
  117. data/test/perf_object.rb +65 -69
  118. data/test/perf_once.rb +59 -0
  119. data/test/perf_parser.rb +183 -0
  120. data/test/perf_saj.rb +46 -54
  121. data/test/perf_scp.rb +58 -69
  122. data/test/perf_simple.rb +41 -39
  123. data/test/perf_strict.rb +74 -82
  124. data/test/perf_wab.rb +67 -69
  125. data/test/prec.rb +5 -5
  126. data/test/sample/change.rb +0 -1
  127. data/test/sample/dir.rb +0 -1
  128. data/test/sample/doc.rb +0 -1
  129. data/test/sample/file.rb +0 -1
  130. data/test/sample/group.rb +0 -1
  131. data/test/sample/hasprops.rb +0 -1
  132. data/test/sample/layer.rb +0 -1
  133. data/test/sample/rect.rb +0 -1
  134. data/test/sample/shape.rb +0 -1
  135. data/test/sample/text.rb +0 -1
  136. data/test/sample.rb +16 -16
  137. data/test/sample_json.rb +8 -8
  138. data/test/test_compat.rb +97 -45
  139. data/test/test_custom.rb +73 -51
  140. data/test/test_debian.rb +7 -10
  141. data/test/test_fast.rb +135 -79
  142. data/test/test_file.rb +41 -30
  143. data/test/test_gc.rb +16 -5
  144. data/test/test_generate.rb +21 -0
  145. data/test/test_hash.rb +15 -5
  146. data/test/test_integer_range.rb +9 -9
  147. data/test/test_null.rb +20 -20
  148. data/test/test_object.rb +99 -96
  149. data/test/test_parser.rb +11 -0
  150. data/test/test_parser_debug.rb +27 -0
  151. data/test/test_parser_saj.rb +337 -0
  152. data/test/test_parser_usual.rb +251 -0
  153. data/test/test_rails.rb +2 -2
  154. data/test/test_saj.rb +10 -8
  155. data/test/test_scp.rb +38 -40
  156. data/test/test_strict.rb +40 -32
  157. data/test/test_various.rb +165 -84
  158. data/test/test_wab.rb +48 -44
  159. data/test/test_writer.rb +47 -47
  160. data/test/tests.rb +13 -5
  161. data/test/tests_mimic.rb +12 -3
  162. data/test/tests_mimic_addition.rb +12 -3
  163. metadata +75 -127
  164. data/ext/oj/hash.c +0 -135
  165. data/ext/oj/hash.h +0 -18
  166. data/ext/oj/hash_test.c +0 -484
  167. data/test/activesupport4/decoding_test.rb +0 -108
  168. data/test/activesupport4/encoding_test.rb +0 -531
  169. data/test/activesupport4/test_helper.rb +0 -41
  170. data/test/activesupport5/test_helper.rb +0 -72
  171. data/test/bar.rb +0 -35
  172. data/test/baz.rb +0 -16
  173. data/test/zoo.rb +0 -13
data/ext/oj/dump.c CHANGED
@@ -1,4 +1,7 @@
1
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"
2
5
 
3
6
  #include <errno.h>
4
7
  #include <math.h>
@@ -7,33 +10,37 @@
7
10
  #include <stdlib.h>
8
11
  #include <string.h>
9
12
  #include <unistd.h>
13
+ #if !IS_WINDOWS
14
+ #include <poll.h>
15
+ #endif
10
16
 
11
- #include "oj.h"
12
17
  #include "cache8.h"
13
- #include "dump.h"
18
+ #include "mem.h"
14
19
  #include "odd.h"
20
+ #include "oj.h"
15
21
  #include "trace.h"
16
22
  #include "util.h"
17
23
 
18
24
  // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
19
- #define OJ_INFINITY (1.0/0.0)
25
+ #define OJ_INFINITY (1.0 / 0.0)
20
26
 
21
27
  #define MAX_DEPTH 1000
22
28
 
23
- static const char inf_val[] = INF_VAL;
24
- static const char ninf_val[] = NINF_VAL;
25
- static const char nan_val[] = NAN_VAL;
29
+ static const char inf_val[] = INF_VAL;
30
+ static const char ninf_val[] = NINF_VAL;
31
+ static const char nan_val[] = NAN_VAL;
26
32
 
27
- typedef unsigned long ulong;
33
+ typedef unsigned long ulong;
28
34
 
29
- static size_t hibit_friendly_size(const uint8_t *str, size_t len);
30
- static size_t xss_friendly_size(const uint8_t *str, size_t len);
31
- static size_t ascii_friendly_size(const uint8_t *str, size_t len);
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);
32
39
 
33
- static const char hex_chars[17] = "0123456789abcdef";
40
+ static const char hex_chars[17] = "0123456789abcdef";
34
41
 
35
42
  // JSON standard except newlines are no escaped
36
- static char newline_friendly_chars[256] = "\
43
+ static char newline_friendly_chars[256] = "\
37
44
  66666666221622666666666666666666\
38
45
  11211111111111111111111111111111\
39
46
  11111111111111111111111111112111\
@@ -44,7 +51,7 @@ static char newline_friendly_chars[256] = "\
44
51
  11111111111111111111111111111111";
45
52
 
46
53
  // JSON standard
47
- static char hibit_friendly_chars[256] = "\
54
+ static char hibit_friendly_chars[256] = "\
48
55
  66666666222622666666666666666666\
49
56
  11211111111111111111111111111111\
50
57
  11111111111111111111111111112111\
@@ -54,9 +61,20 @@ static char hibit_friendly_chars[256] = "\
54
61
  11111111111111111111111111111111\
55
62
  11111111111111111111111111111111";
56
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
+
57
75
  // High bit set characters are always encoded as unicode. Worse case is 3
58
76
  // bytes per character in the output. That makes this conservative.
59
- static char ascii_friendly_chars[256] = "\
77
+ static char ascii_friendly_chars[256] = "\
60
78
  66666666222622666666666666666666\
61
79
  11211111111111111111111111111111\
62
80
  11111111111111111111111111112111\
@@ -67,7 +85,7 @@ static char ascii_friendly_chars[256] = "\
67
85
  33333333333333333333333333333333";
68
86
 
69
87
  // XSS safe mode
70
- static char xss_friendly_chars[256] = "\
88
+ static char xss_friendly_chars[256] = "\
71
89
  66666666222622666666666666666666\
72
90
  11211161111111121111111111116161\
73
91
  11111111111111111111111111112111\
@@ -78,7 +96,7 @@ static char xss_friendly_chars[256] = "\
78
96
  33333333333333333333333333333333";
79
97
 
80
98
  // JSON XSS combo
81
- static char hixss_friendly_chars[256] = "\
99
+ static char hixss_friendly_chars[256] = "\
82
100
  66666666222622666666666666666666\
83
101
  11211111111111111111111111111111\
84
102
  11111111111111111111111111112111\
@@ -89,7 +107,7 @@ static char hixss_friendly_chars[256] = "\
89
107
  11611111111111111111111111111111";
90
108
 
91
109
  // Rails XSS combo
92
- static char rails_xss_friendly_chars[256] = "\
110
+ static char rails_xss_friendly_chars[256] = "\
93
111
  66666666222622666666666666666666\
94
112
  11211161111111111111111111116161\
95
113
  11111111111111111111111111112111\
@@ -100,7 +118,7 @@ static char rails_xss_friendly_chars[256] = "\
100
118
  11611111111111111111111111111111";
101
119
 
102
120
  // Rails HTML non-escape
103
- static char rails_friendly_chars[256] = "\
121
+ static char rails_friendly_chars[256] = "\
104
122
  66666666222622666666666666666666\
105
123
  11211111111111111111111111111111\
106
124
  11111111111111111111111111112111\
@@ -110,248 +128,232 @@ static char rails_friendly_chars[256] = "\
110
128
  11111111111111111111111111111111\
111
129
  11111111111111111111111111111111";
112
130
 
113
- static void
114
- raise_strict(VALUE obj) {
131
+ static void raise_strict(VALUE obj) {
115
132
  rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.", rb_class2name(rb_obj_class(obj)));
116
133
  }
117
134
 
118
- inline static size_t
119
- newline_friendly_size(const uint8_t *str, size_t len) {
120
- size_t size = 0;
121
- size_t i = len;
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;
122
138
 
123
- for (; 0 < i; str++, i--) {
124
- size += newline_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++];
125
147
  }
126
148
  return size - len * (size_t)'0';
127
149
  }
128
150
 
129
- inline static size_t
130
- hibit_friendly_size(const uint8_t *str, size_t len) {
131
- size_t size = 0;
132
- size_t i = len;
133
-
134
- for (; 0 < i; str++, i--) {
135
- size += hibit_friendly_chars[*str];
136
- }
137
- 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);
138
153
  }
139
154
 
140
- inline static size_t
141
- ascii_friendly_size(const uint8_t *str, size_t len) {
142
- size_t size = 0;
143
- size_t i = len;
155
+ inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
156
+ return calculate_string_size(str, len, hibit_friendly_chars);
157
+ }
144
158
 
145
- for (; 0 < i; str++, i--) {
146
- size += ascii_friendly_chars[*str];
147
- }
148
- return size - len * (size_t)'0';
159
+ inline static size_t slash_friendly_size(const uint8_t *str, size_t len) {
160
+ return calculate_string_size(str, len, slash_friendly_chars);
149
161
  }
150
162
 
151
- inline static size_t
152
- xss_friendly_size(const uint8_t *str, size_t len) {
153
- size_t size = 0;
154
- size_t i = len;
163
+ inline static size_t ascii_friendly_size(const uint8_t *str, size_t len) {
164
+ return calculate_string_size(str, len, ascii_friendly_chars);
165
+ }
155
166
 
156
- for (; 0 < i; str++, i--) {
157
- size += xss_friendly_chars[*str];
158
- }
159
- return size - len * (size_t)'0';
167
+ inline static size_t xss_friendly_size(const uint8_t *str, size_t len) {
168
+ return calculate_string_size(str, len, xss_friendly_chars);
160
169
  }
161
170
 
162
- inline static size_t
163
- hixss_friendly_size(const uint8_t *str, size_t len) {
164
- size_t size = 0;
165
- size_t i = len;
166
- bool check = false;
171
+ inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
172
+ size_t size = 0;
173
+ size_t i = len;
174
+ bool check = false;
167
175
 
168
176
  for (; 0 < i; str++, i--) {
169
- size += hixss_friendly_chars[*str];
170
- if (0 != (0x80 & *str)) {
171
- check = true;
172
- }
177
+ size += hixss_friendly_chars[*str];
178
+ if (0 != (0x80 & *str)) {
179
+ check = true;
180
+ }
173
181
  }
174
182
  return size - len * (size_t)'0' + check;
175
183
  }
176
184
 
177
- inline static long
178
- rails_xss_friendly_size(const uint8_t *str, size_t len) {
179
- long size = 0;
180
- size_t i = len;
181
- uint8_t hi = 0;
185
+ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
186
+ long size = 0;
187
+ size_t i = len;
188
+ uint8_t hi = 0;
182
189
 
183
190
  for (; 0 < i; str++, i--) {
184
- size += rails_xss_friendly_chars[*str];
185
- hi |= *str & 0x80;
191
+ size += rails_xss_friendly_chars[*str];
192
+ hi |= *str & 0x80;
186
193
  }
187
194
  if (0 == hi) {
188
- return size - len * (size_t)'0';
195
+ return size - len * (size_t)'0';
189
196
  }
190
197
  return -(size - len * (size_t)'0');
191
198
  }
192
199
 
193
- inline static size_t
194
- rails_friendly_size(const uint8_t *str, size_t len) {
195
- size_t size = 0;
196
- size_t i = len;
200
+ inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
201
+ long size = 0;
202
+ size_t i = len;
203
+ uint8_t hi = 0;
197
204
 
198
205
  for (; 0 < i; str++, i--) {
199
- size += rails_friendly_chars[*str];
206
+ size += rails_friendly_chars[*str];
207
+ hi |= *str & 0x80;
200
208
  }
201
- return size - len * (size_t)'0';
209
+ if (0 == hi) {
210
+ return size - len * (size_t)'0';
211
+ }
212
+ return -(size - len * (size_t)'0');
202
213
  }
203
214
 
204
- const char*
205
- oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
206
- const char *str = NULL;
215
+ const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
216
+ const char *str = NULL;
207
217
 
208
218
  if (AutoNan == opt) {
209
- switch (mode) {
210
- case CompatMode: opt = WordNan; break;
211
- case StrictMode: opt = RaiseNan; break;
212
- default: break;
213
- }
219
+ switch (mode) {
220
+ case CompatMode: opt = WordNan; break;
221
+ case StrictMode: opt = RaiseNan; break;
222
+ default: break;
223
+ }
214
224
  }
215
225
  switch (opt) {
216
- case RaiseNan:
217
- raise_strict(obj);
218
- break;
226
+ case RaiseNan: raise_strict(obj); break;
219
227
  case WordNan:
220
- if (plus) {
221
- str = "Infinity";
222
- *lenp = 8;
223
- } else {
224
- str = "-Infinity";
225
- *lenp = 9;
226
- }
227
- break;
228
+ if (plus) {
229
+ str = "Infinity";
230
+ *lenp = 8;
231
+ } else {
232
+ str = "-Infinity";
233
+ *lenp = 9;
234
+ }
235
+ break;
228
236
  case NullNan:
229
- str = "null";
230
- *lenp = 4;
231
- break;
237
+ str = "null";
238
+ *lenp = 4;
239
+ break;
232
240
  case HugeNan:
233
241
  default:
234
- if (plus) {
235
- str = inf_val;
236
- *lenp = sizeof(inf_val) - 1;
237
- } else {
238
- str = ninf_val;
239
- *lenp = sizeof(ninf_val) - 1;
240
- }
241
- break;
242
+ if (plus) {
243
+ str = inf_val;
244
+ *lenp = sizeof(inf_val) - 1;
245
+ } else {
246
+ str = ninf_val;
247
+ *lenp = sizeof(ninf_val) - 1;
248
+ }
249
+ break;
242
250
  }
243
251
  return str;
244
252
  }
245
253
 
246
- inline static void
247
- dump_hex(uint8_t c, Out out) {
248
- uint8_t d = (c >> 4) & 0x0F;
254
+ inline static void dump_hex(uint8_t c, Out out) {
255
+ uint8_t d = (c >> 4) & 0x0F;
249
256
 
250
257
  *out->cur++ = hex_chars[d];
251
- d = c & 0x0F;
258
+ d = c & 0x0F;
252
259
  *out->cur++ = hex_chars[d];
253
260
  }
254
261
 
255
- static void
256
- raise_invalid_unicode(const char *str, int len, int pos) {
257
- char c;
258
- char code[32];
259
- char *cp = code;
260
- int i;
261
- uint8_t d;
262
+ static void raise_invalid_unicode(const char *str, int len, int pos) {
263
+ char c;
264
+ char code[32];
265
+ char *cp = code;
266
+ int i;
267
+ uint8_t d;
262
268
 
263
269
  *cp++ = '[';
264
270
  for (i = pos; i < len && i - pos < 5; i++) {
265
- c = str[i];
266
- d = (c >> 4) & 0x0F;
267
- *cp++ = hex_chars[d];
268
- d = c & 0x0F;
269
- *cp++ = hex_chars[d];
270
- *cp++ = ' ';
271
+ c = str[i];
272
+ d = (c >> 4) & 0x0F;
273
+ *cp++ = hex_chars[d];
274
+ d = c & 0x0F;
275
+ *cp++ = hex_chars[d];
276
+ *cp++ = ' ';
271
277
  }
272
278
  cp--;
273
279
  *cp++ = ']';
274
- *cp = '\0';
280
+ *cp = '\0';
275
281
  rb_raise(oj_json_generator_error_class, "Invalid Unicode %s at %d", code, pos);
276
282
  }
277
283
 
278
- static const char*
279
- dump_unicode(const char *str, const char *end, Out out, const char *orig) {
280
- uint32_t code = 0;
281
- uint8_t b = *(uint8_t*)str;
282
- int i, cnt;
284
+ static const char *dump_unicode(const char *str, const char *end, Out out, const char *orig) {
285
+ uint32_t code = 0;
286
+ uint8_t b = *(uint8_t *)str;
287
+ int i, cnt;
283
288
 
284
289
  if (0xC0 == (0xE0 & b)) {
285
- cnt = 1;
286
- code = b & 0x0000001F;
290
+ cnt = 1;
291
+ code = b & 0x0000001F;
287
292
  } else if (0xE0 == (0xF0 & b)) {
288
- cnt = 2;
289
- code = b & 0x0000000F;
293
+ cnt = 2;
294
+ code = b & 0x0000000F;
290
295
  } else if (0xF0 == (0xF8 & b)) {
291
- cnt = 3;
292
- code = b & 0x00000007;
296
+ cnt = 3;
297
+ code = b & 0x00000007;
293
298
  } else if (0xF8 == (0xFC & b)) {
294
- cnt = 4;
295
- code = b & 0x00000003;
299
+ cnt = 4;
300
+ code = b & 0x00000003;
296
301
  } else if (0xFC == (0xFE & b)) {
297
- cnt = 5;
298
- code = b & 0x00000001;
302
+ cnt = 5;
303
+ code = b & 0x00000001;
299
304
  } else {
300
- cnt = 0;
301
- raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
305
+ cnt = 0;
306
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
302
307
  }
303
308
  str++;
304
309
  for (; 0 < cnt; cnt--, str++) {
305
- b = *(uint8_t*)str;
306
- if (end <= str || 0x80 != (0xC0 & b)) {
307
- raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
308
- }
309
- code = (code << 6) | (b & 0x0000003F);
310
+ b = *(uint8_t *)str;
311
+ if (end <= str || 0x80 != (0xC0 & b)) {
312
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
313
+ }
314
+ code = (code << 6) | (b & 0x0000003F);
310
315
  }
311
316
  if (0x0000FFFF < code) {
312
- uint32_t c1;
313
-
314
- code -= 0x00010000;
315
- c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
316
- code = (code & 0x000003FF) + 0x0000DC00;
317
- *out->cur++ = '\\';
318
- *out->cur++ = 'u';
319
- for (i = 3; 0 <= i; i--) {
320
- *out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
321
- }
317
+ uint32_t c1;
318
+
319
+ code -= 0x00010000;
320
+ c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
321
+ code = (code & 0x000003FF) + 0x0000DC00;
322
+ APPEND_CHARS(out->cur, "\\u", 2);
323
+ for (i = 3; 0 <= i; i--) {
324
+ *out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
325
+ }
322
326
  }
323
- *out->cur++ = '\\';
324
- *out->cur++ = 'u';
327
+ APPEND_CHARS(out->cur, "\\u", 2);
325
328
  for (i = 3; 0 <= i; i--) {
326
- *out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
329
+ *out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
327
330
  }
328
331
  return str - 1;
329
332
  }
330
333
 
331
- static const char*
332
- check_unicode(const char *str, const char *end, const char *orig) {
333
- uint8_t b = *(uint8_t*)str;
334
- int cnt = 0;
334
+ static const char *check_unicode(const char *str, const char *end, const char *orig) {
335
+ uint8_t b = *(uint8_t *)str;
336
+ int cnt = 0;
335
337
 
336
338
  if (0xC0 == (0xE0 & b)) {
337
- cnt = 1;
339
+ cnt = 1;
338
340
  } else if (0xE0 == (0xF0 & b)) {
339
- cnt = 2;
341
+ cnt = 2;
340
342
  } else if (0xF0 == (0xF8 & b)) {
341
- cnt = 3;
343
+ cnt = 3;
342
344
  } else if (0xF8 == (0xFC & b)) {
343
- cnt = 4;
345
+ cnt = 4;
344
346
  } else if (0xFC == (0xFE & b)) {
345
- cnt = 5;
347
+ cnt = 5;
346
348
  } else {
347
- raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
349
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
348
350
  }
349
351
  str++;
350
352
  for (; 0 < cnt; cnt--, str++) {
351
- b = *(uint8_t*)str;
352
- if (end <= str || 0x80 != (0xC0 & b)) {
353
- raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
354
- }
353
+ b = *(uint8_t *)str;
354
+ if (end <= str || 0x80 != (0xC0 & b)) {
355
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
356
+ }
355
357
  }
356
358
  return str;
357
359
  }
@@ -359,901 +361,894 @@ check_unicode(const char *str, const char *end, const char *orig) {
359
361
  // Returns 0 if not using circular references, -1 if no further writing is
360
362
  // needed (duplicate), and a positive value if the object was added to the
361
363
  // cache.
362
- long
363
- oj_check_circular(VALUE obj, Out out) {
364
- slot_t id = 0;
365
- slot_t *slot;
364
+ long oj_check_circular(VALUE obj, Out out) {
365
+ slot_t id = 0;
366
+ slot_t *slot;
366
367
 
367
368
  if (Yes == out->opts->circular) {
368
- if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
369
- out->circ_cnt++;
370
- id = out->circ_cnt;
371
- *slot = id;
372
- } else {
373
- if (ObjectMode == out->opts->mode) {
374
- assure_size(out, 18);
375
- *out->cur++ = '"';
376
- *out->cur++ = '^';
377
- *out->cur++ = 'r';
378
- dump_ulong(id, out);
379
- *out->cur++ = '"';
380
- }
381
- return -1;
382
- }
369
+ if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
370
+ out->circ_cnt++;
371
+ id = out->circ_cnt;
372
+ *slot = id;
373
+ } else {
374
+ if (ObjectMode == out->opts->mode) {
375
+ assure_size(out, 18);
376
+ APPEND_CHARS(out->cur, "\"^r", 3);
377
+ dump_ulong(id, out);
378
+ *out->cur++ = '"';
379
+ }
380
+ return -1;
381
+ }
383
382
  }
384
383
  return (long)id;
385
384
  }
386
385
 
387
- void
388
- oj_dump_time(VALUE obj, Out out, int withZone) {
389
- char buf[64];
390
- char *b = buf + sizeof(buf) - 1;
391
- long size;
392
- char *dot;
393
- int neg = 0;
394
- long one = 1000000000;
395
- long long sec;
396
- long long nsec;
397
-
398
- #ifdef HAVE_RB_TIME_TIMESPEC
386
+ void oj_dump_time(VALUE obj, Out out, int withZone) {
387
+ char buf[64];
388
+ char *b = buf + sizeof(buf) - 1;
389
+ long size;
390
+ char *dot;
391
+ int neg = 0;
392
+ long one = 1000000000;
393
+ long long sec;
394
+ long long nsec;
395
+
399
396
  // rb_time_timespec as well as rb_time_timeeval have a bug that causes an
400
397
  // exception to be raised if a time is before 1970 on 32 bit systems so
401
398
  // check the timespec size and use the ruby calls if a 32 bit system.
402
399
  if (16 <= sizeof(struct timespec)) {
403
- struct timespec ts = rb_time_timespec(obj);
400
+ struct timespec ts = rb_time_timespec(obj);
404
401
 
405
- sec = (long long)ts.tv_sec;
406
- nsec = ts.tv_nsec;
402
+ sec = (long long)ts.tv_sec;
403
+ nsec = ts.tv_nsec;
407
404
  } else {
408
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
409
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
405
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
406
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
410
407
  }
411
- #else
412
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
413
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
414
- #endif
415
408
 
416
409
  *b-- = '\0';
417
410
  if (withZone) {
418
- long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
419
- int zneg = (0 > tzsecs);
420
-
421
- if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
422
- tzsecs = 86400;
423
- }
424
- if (zneg) {
425
- tzsecs = -tzsecs;
426
- }
427
- if (0 == tzsecs) {
428
- *b-- = '0';
429
- } else {
430
- for (; 0 < tzsecs; b--, tzsecs /= 10) {
431
- *b = '0' + (tzsecs % 10);
432
- }
433
- if (zneg) {
434
- *b-- = '-';
435
- }
436
- }
437
- *b-- = 'e';
411
+ long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
412
+ int zneg = (0 > tzsecs);
413
+
414
+ if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
415
+ tzsecs = 86400;
416
+ }
417
+ if (zneg) {
418
+ tzsecs = -tzsecs;
419
+ }
420
+ if (0 == tzsecs) {
421
+ *b-- = '0';
422
+ } else {
423
+ for (; 0 < tzsecs; b--, tzsecs /= 10) {
424
+ *b = '0' + (tzsecs % 10);
425
+ }
426
+ if (zneg) {
427
+ *b-- = '-';
428
+ }
429
+ }
430
+ *b-- = 'e';
438
431
  }
439
432
  if (0 > sec) {
440
- neg = 1;
441
- sec = -sec;
442
- if (0 < nsec) {
443
- nsec = 1000000000 - nsec;
444
- sec--;
445
- }
433
+ neg = 1;
434
+ sec = -sec;
435
+ if (0 < nsec) {
436
+ nsec = 1000000000 - nsec;
437
+ sec--;
438
+ }
446
439
  }
447
440
  dot = b - 9;
448
441
  if (0 < out->opts->sec_prec) {
449
- if (9 > out->opts->sec_prec) {
450
- int i;
451
-
452
- for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
453
- dot++;
454
- nsec = (nsec + 5) / 10;
455
- one /= 10;
456
- }
457
- }
458
- if (one <= nsec) {
459
- nsec -= one;
460
- sec++;
461
- }
462
- for (; dot < b; b--, nsec /= 10) {
463
- *b = '0' + (nsec % 10);
464
- }
465
- *b-- = '.';
442
+ if (9 > out->opts->sec_prec) {
443
+ int i;
444
+
445
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
446
+ dot++;
447
+ nsec = (nsec + 5) / 10;
448
+ one /= 10;
449
+ }
450
+ }
451
+ if (one <= nsec) {
452
+ nsec -= one;
453
+ sec++;
454
+ }
455
+ for (; dot < b; b--, nsec /= 10) {
456
+ *b = '0' + (nsec % 10);
457
+ }
458
+ *b-- = '.';
466
459
  }
467
460
  if (0 == sec) {
468
- *b-- = '0';
461
+ *b-- = '0';
469
462
  } else {
470
- for (; 0 < sec; b--, sec /= 10) {
471
- *b = '0' + (sec % 10);
472
- }
463
+ for (; 0 < sec; b--, sec /= 10) {
464
+ *b = '0' + (sec % 10);
465
+ }
473
466
  }
474
467
  if (neg) {
475
- *b-- = '-';
468
+ *b-- = '-';
476
469
  }
477
470
  b++;
478
471
  size = sizeof(buf) - (b - buf) - 1;
479
472
  assure_size(out, size);
480
- memcpy(out->cur, b, size);
481
- out->cur += size;
473
+ APPEND_CHARS(out->cur, b, size);
482
474
  *out->cur = '\0';
483
475
  }
484
476
 
485
- void
486
- oj_dump_ruby_time(VALUE obj, Out out) {
487
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
477
+ void oj_dump_ruby_time(VALUE obj, Out out) {
478
+ volatile VALUE rstr = oj_safe_string_convert(obj);
488
479
 
489
- oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
480
+ oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
490
481
  }
491
482
 
492
- void
493
- oj_dump_xml_time(VALUE obj, Out out) {
494
- char buf[64];
495
- struct _timeInfo ti;
496
- long one = 1000000000;
497
- int64_t sec;
498
- long long nsec;
499
- long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
500
- int tzhour, tzmin;
501
- char tzsign = '+';
502
-
503
- #ifdef HAVE_RB_TIME_TIMESPEC
483
+ void oj_dump_xml_time(VALUE obj, Out out) {
484
+ char buf[64];
485
+ struct _timeInfo ti;
486
+ long one = 1000000000;
487
+ int64_t sec;
488
+ long long nsec;
489
+ long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
490
+ int tzhour, tzmin;
491
+ char tzsign = '+';
492
+
504
493
  if (16 <= sizeof(struct timespec)) {
505
- struct timespec ts = rb_time_timespec(obj);
494
+ struct timespec ts = rb_time_timespec(obj);
506
495
 
507
- sec = ts.tv_sec;
508
- nsec = ts.tv_nsec;
496
+ sec = ts.tv_sec;
497
+ nsec = ts.tv_nsec;
509
498
  } else {
510
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
511
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
499
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
500
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
512
501
  }
513
- #else
514
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
515
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
516
- #endif
517
502
 
518
503
  assure_size(out, 36);
519
504
  if (9 > out->opts->sec_prec) {
520
- int i;
521
-
522
- // This is pretty lame but to be compatible with rails and active
523
- // support rounding is not done but instead a floor is done when
524
- // second precision is 3 just to be like rails. sigh.
525
- if (3 == out->opts->sec_prec) {
526
- nsec /= 1000000;
527
- one = 1000;
528
- } else {
529
- for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
530
- nsec = (nsec + 5) / 10;
531
- one /= 10;
532
- }
533
- if (one <= nsec) {
534
- nsec -= one;
535
- sec++;
536
- }
537
- }
505
+ int i;
506
+
507
+ // This is pretty lame but to be compatible with rails and active
508
+ // support rounding is not done but instead a floor is done when
509
+ // second precision is 3 just to be like rails. sigh.
510
+ if (3 == out->opts->sec_prec) {
511
+ nsec /= 1000000;
512
+ one = 1000;
513
+ } else {
514
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
515
+ nsec = (nsec + 5) / 10;
516
+ one /= 10;
517
+ }
518
+ if (one <= nsec) {
519
+ nsec -= one;
520
+ sec++;
521
+ }
522
+ }
538
523
  }
539
524
  // 2012-01-05T23:58:07.123456000+09:00
540
- //tm = localtime(&sec);
525
+ // tm = localtime(&sec);
541
526
  sec += tzsecs;
542
527
  sec_as_time((int64_t)sec, &ti);
543
528
  if (0 > tzsecs) {
544
529
  tzsign = '-';
545
530
  tzhour = (int)(tzsecs / -3600);
546
- tzmin = (int)(tzsecs / -60) - (tzhour * 60);
531
+ tzmin = (int)(tzsecs / -60) - (tzhour * 60);
547
532
  } else {
548
533
  tzhour = (int)(tzsecs / 3600);
549
- tzmin = (int)(tzsecs / 60) - (tzhour * 60);
534
+ tzmin = (int)(tzsecs / 60) - (tzhour * 60);
550
535
  }
551
536
  if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
552
- if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
553
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
554
- oj_dump_cstr(buf, 20, 0, 0, out);
555
- } else {
556
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec,
557
- tzsign, tzhour, tzmin);
558
- oj_dump_cstr(buf, 25, 0, 0, out);
559
- }
537
+ if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
538
+ int len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
539
+ oj_dump_cstr(buf, len, 0, 0, out);
540
+ } else {
541
+ int len = sprintf(buf,
542
+ "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
543
+ ti.year,
544
+ ti.mon,
545
+ ti.day,
546
+ ti.hour,
547
+ ti.min,
548
+ ti.sec,
549
+ tzsign,
550
+ tzhour,
551
+ tzmin);
552
+ oj_dump_cstr(buf, len, 0, 0, out);
553
+ }
560
554
  } else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
561
- char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
562
- int len = 30;
563
-
564
- if (9 > out->opts->sec_prec) {
565
- format[32] = '0' + out->opts->sec_prec;
566
- len -= 9 - out->opts->sec_prec;
567
- }
568
- sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
569
- oj_dump_cstr(buf, len, 0, 0, out);
555
+ char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
556
+ int len;
557
+
558
+ if (9 > out->opts->sec_prec) {
559
+ format[32] = '0' + out->opts->sec_prec;
560
+ }
561
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
562
+ oj_dump_cstr(buf, len, 0, 0, out);
570
563
  } else {
571
- char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
572
- int len = 35;
573
-
574
- if (9 > out->opts->sec_prec) {
575
- format[32] = '0' + out->opts->sec_prec;
576
- len -= 9 - out->opts->sec_prec;
577
- }
578
- sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
579
- oj_dump_cstr(buf, len, 0, 0, out);
564
+ char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
565
+ int len;
566
+
567
+ if (9 > out->opts->sec_prec) {
568
+ format[32] = '0' + out->opts->sec_prec;
569
+ }
570
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
571
+ oj_dump_cstr(buf, len, 0, 0, out);
580
572
  }
581
573
  }
582
574
 
583
- void
584
- oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
575
+ void oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
585
576
  oj_dump_obj_to_json_using_params(obj, copts, out, 0, 0);
586
577
  }
587
578
 
588
- void
589
- oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
579
+ void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
590
580
  if (0 == out->buf) {
591
- out->buf = ALLOC_N(char, 4096);
592
- out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
593
- out->allocated = true;
581
+ oj_out_init(out);
594
582
  }
595
- out->cur = out->buf;
596
583
  out->circ_cnt = 0;
597
- out->opts = copts;
584
+ out->opts = copts;
598
585
  out->hash_cnt = 0;
599
- out->indent = copts->indent;
600
- out->argc = argc;
601
- out->argv = argv;
602
- out->ropts = NULL;
586
+ out->indent = copts->indent;
587
+ out->argc = argc;
588
+ out->argv = argv;
589
+ out->ropts = NULL;
603
590
  if (Yes == copts->circular) {
604
- oj_cache8_new(&out->circ_cache);
591
+ oj_cache8_new(&out->circ_cache);
605
592
  }
606
593
  switch (copts->mode) {
607
- case StrictMode: oj_dump_strict_val(obj, 0, out); break;
608
- case NullMode: oj_dump_null_val(obj, 0, out); break;
609
- case ObjectMode: oj_dump_obj_val(obj, 0, out); break;
610
- case CompatMode: oj_dump_compat_val(obj, 0, out, Yes == copts->to_json); break;
611
- case RailsMode: oj_dump_rails_val(obj, 0, out); break;
612
- case CustomMode: oj_dump_custom_val(obj, 0, out, true); break;
613
- case WabMode: oj_dump_wab_val(obj, 0, out); break;
614
- default: oj_dump_custom_val(obj, 0, out, true); break;
594
+ case StrictMode: oj_dump_strict_val(obj, 0, out); break;
595
+ case NullMode: oj_dump_null_val(obj, 0, out); break;
596
+ case ObjectMode: oj_dump_obj_val(obj, 0, out); break;
597
+ case CompatMode: oj_dump_compat_val(obj, 0, out, Yes == copts->to_json); break;
598
+ case RailsMode: oj_dump_rails_val(obj, 0, out); break;
599
+ case CustomMode: oj_dump_custom_val(obj, 0, out, true); break;
600
+ case WabMode: oj_dump_wab_val(obj, 0, out); break;
601
+ default: oj_dump_custom_val(obj, 0, out, true); break;
615
602
  }
616
603
  if (0 < out->indent) {
617
- switch (*(out->cur - 1)) {
618
- case ']':
619
- case '}':
620
- assure_size(out, 1);
621
- *out->cur++ = '\n';
622
- default:
623
- break;
624
- }
604
+ switch (*(out->cur - 1)) {
605
+ case ']':
606
+ case '}': assure_size(out, 1); *out->cur++ = '\n';
607
+ default: break;
608
+ }
625
609
  }
626
610
  *out->cur = '\0';
627
611
  if (Yes == copts->circular) {
628
- oj_cache8_delete(out->circ_cache);
612
+ oj_cache8_delete(out->circ_cache);
629
613
  }
630
614
  }
631
615
 
632
- void
633
- oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
634
- char buf[4096];
616
+ void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
635
617
  struct _out out;
636
- size_t size;
637
- FILE *f;
638
- int ok;
618
+ size_t size;
619
+ FILE *f;
620
+ int ok;
621
+
622
+ oj_out_init(&out);
639
623
 
640
- out.buf = buf;
641
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
642
- out.allocated = false;
643
624
  out.omit_nil = copts->dump_opts.omit_nil;
644
625
  oj_dump_obj_to_json(obj, copts, &out);
645
626
  size = out.cur - out.buf;
646
627
  if (0 == (f = fopen(path, "w"))) {
647
- if (out.allocated) {
648
- xfree(out.buf);
649
- }
650
- rb_raise(rb_eIOError, "%s", strerror(errno));
628
+ oj_out_free(&out);
629
+ rb_raise(rb_eIOError, "%s", strerror(errno));
651
630
  }
652
631
  ok = (size == fwrite(out.buf, 1, size, f));
653
- if (out.allocated) {
654
- xfree(out.buf);
632
+
633
+ oj_out_free(&out);
634
+
635
+ if (!ok) {
636
+ int err = ferror(f);
637
+ fclose(f);
638
+
639
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
655
640
  }
656
641
  fclose(f);
657
- if (!ok) {
658
- int err = ferror(f);
642
+ }
659
643
 
660
- rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
644
+ #if !IS_WINDOWS
645
+ static void write_ready(int fd) {
646
+ struct pollfd pp;
647
+ int i;
648
+
649
+ pp.fd = fd;
650
+ pp.events = POLLERR | POLLOUT;
651
+ pp.revents = 0;
652
+ if (0 >= (i = poll(&pp, 1, 5000))) {
653
+ if (0 == i || EAGAIN == errno) {
654
+ rb_raise(rb_eIOError, "write timed out");
655
+ }
656
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
661
657
  }
662
658
  }
659
+ #endif
663
660
 
664
- void
665
- oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
666
- char buf[4096];
661
+ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
667
662
  struct _out out;
668
- ssize_t size;
669
- VALUE clas = rb_obj_class(stream);
663
+ ssize_t size;
664
+ VALUE clas = rb_obj_class(stream);
670
665
  #if !IS_WINDOWS
671
- int fd;
672
- VALUE s;
666
+ int fd;
667
+ VALUE s;
673
668
  #endif
674
669
 
675
- out.buf = buf;
676
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
677
- out.allocated = false;
670
+ oj_out_init(&out);
671
+
678
672
  out.omit_nil = copts->dump_opts.omit_nil;
679
673
  oj_dump_obj_to_json(obj, copts, &out);
680
674
  size = out.cur - out.buf;
681
675
  if (oj_stringio_class == clas) {
682
- rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
676
+ rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
683
677
  #if !IS_WINDOWS
684
- } else if (rb_respond_to(stream, oj_fileno_id) &&
685
- Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
686
- 0 != (fd = FIX2INT(s))) {
687
- if (size != write(fd, out.buf, size)) {
688
- if (out.allocated) {
689
- xfree(out.buf);
690
- }
691
- rb_raise(rb_eIOError, "Write failed. [%d:%s]", errno, strerror(errno));
692
- }
678
+ } else if (rb_respond_to(stream, oj_fileno_id) && Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
679
+ 0 != (fd = FIX2INT(s))) {
680
+ ssize_t cnt;
681
+ ssize_t total = 0;
682
+
683
+ while (true) {
684
+ if (0 > (cnt = write(fd, out.buf + total, size - total))) {
685
+ if (EAGAIN != errno) {
686
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
687
+ break;
688
+ }
689
+ }
690
+ total += cnt;
691
+ if (size <= total) {
692
+ // Completed
693
+ break;
694
+ }
695
+ write_ready(fd);
696
+ }
693
697
  #endif
694
698
  } else if (rb_respond_to(stream, oj_write_id)) {
695
- rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
699
+ rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
696
700
  } else {
697
- if (out.allocated) {
698
- xfree(out.buf);
699
- }
700
- rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
701
- }
702
- if (out.allocated) {
703
- xfree(out.buf);
701
+ oj_out_free(&out);
702
+ rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
704
703
  }
704
+ oj_out_free(&out);
705
705
  }
706
706
 
707
- void
708
- oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
709
- rb_encoding *enc = rb_to_encoding(rb_obj_encoding(obj));
707
+ void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
708
+ int idx = RB_ENCODING_GET(obj);
710
709
 
711
- if (rb_utf8_encoding() != enc) {
712
- obj = rb_str_conv_enc(obj, enc, rb_utf8_encoding());
710
+ if (oj_utf8_encoding_index != idx) {
711
+ rb_encoding *enc = rb_enc_from_index(idx);
712
+ obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
713
713
  }
714
- oj_dump_cstr(rb_string_value_ptr((VALUE*)&obj), (int)RSTRING_LEN(obj), 0, 0, out);
714
+ oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
715
715
  }
716
716
 
717
- void
718
- oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
719
- // This causes a memory leak in 2.5.1. Maybe in other versions as well.
720
- //const char *sym = rb_id2name(SYM2ID(obj));
721
-
722
- volatile VALUE s = rb_sym_to_s(obj);
717
+ void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
718
+ volatile VALUE s = rb_sym2str(obj);
723
719
 
724
- oj_dump_cstr(rb_string_value_ptr((VALUE*)&s), (int)RSTRING_LEN(s), 0, 0, out);
720
+ oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 0, 0, out);
725
721
  }
726
722
 
727
- static void
728
- debug_raise(const char *orig, size_t cnt, int line) {
729
- char buf[1024];
730
- char *b = buf;
731
- const char *s = orig;
732
- const char *s_end = s + cnt;
723
+ static void debug_raise(const char *orig, size_t cnt, int line) {
724
+ char buf[1024];
725
+ char *b = buf;
726
+ const char *s = orig;
727
+ const char *s_end = s + cnt;
733
728
 
734
729
  if (32 < s_end - s) {
735
- s_end = s + 32;
730
+ s_end = s + 32;
736
731
  }
737
732
  for (; s < s_end; s++) {
738
- b += sprintf(b, " %02x", *s);
733
+ b += sprintf(b, " %02x", *s);
739
734
  }
740
735
  *b = '\0';
741
736
  rb_raise(oj_json_generator_error_class, "Partial character in string. %s @ %d", buf, line);
742
737
  }
743
738
 
744
- void
745
- oj_dump_raw_json(VALUE obj, int depth, Out out) {
739
+ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
746
740
  if (oj_string_writer_class == rb_obj_class(obj)) {
747
- StrWriter sw = (StrWriter)DATA_PTR(obj);
748
- size_t len = sw->out.cur - sw->out.buf;
741
+ StrWriter sw;
742
+ size_t len;
749
743
 
750
- if (0 < len) {
751
- len--;
752
- }
753
- oj_dump_raw(sw->out.buf, len, out);
744
+ sw = oj_str_writer_unwrap(obj);
745
+ len = sw->out.cur - sw->out.buf;
746
+
747
+ if (0 < len) {
748
+ len--;
749
+ }
750
+ oj_dump_raw(sw->out.buf, len, out);
754
751
  } else {
755
- volatile VALUE jv;
756
-
757
- if (Yes == out->opts->trace) {
758
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
759
- }
760
- jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
761
- if (Yes == out->opts->trace) {
762
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
763
- }
764
- oj_dump_raw(rb_string_value_ptr((VALUE*)&jv), (size_t)RSTRING_LEN(jv), out);
752
+ volatile VALUE jv;
753
+
754
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyIn);
755
+ jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
756
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyOut);
757
+ oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
765
758
  }
766
759
  }
767
760
 
768
- void
769
- oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
770
- size_t size;
771
- char *cmap;
772
- const char *orig = str;
773
- bool has_hi = false;
761
+ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
762
+ size_t size;
763
+ char *cmap;
764
+ const char *orig = str;
765
+ bool has_hi = false;
766
+ bool do_unicode_validation = false;
774
767
 
775
768
  switch (out->opts->escape_mode) {
776
769
  case NLEsc:
777
- cmap = newline_friendly_chars;
778
- size = newline_friendly_size((uint8_t*)str, cnt);
779
- break;
770
+ cmap = newline_friendly_chars;
771
+ size = newline_friendly_size((uint8_t *)str, cnt);
772
+ break;
780
773
  case ASCIIEsc:
781
- cmap = ascii_friendly_chars;
782
- size = ascii_friendly_size((uint8_t*)str, cnt);
783
- break;
774
+ cmap = ascii_friendly_chars;
775
+ size = ascii_friendly_size((uint8_t *)str, cnt);
776
+ break;
777
+ case SlashEsc:
778
+ has_hi = true;
779
+ cmap = slash_friendly_chars;
780
+ size = slash_friendly_size((uint8_t *)str, cnt);
781
+ break;
784
782
  case XSSEsc:
785
- cmap = xss_friendly_chars;
786
- size = xss_friendly_size((uint8_t*)str, cnt);
787
- break;
783
+ cmap = xss_friendly_chars;
784
+ size = xss_friendly_size((uint8_t *)str, cnt);
785
+ break;
788
786
  case JXEsc:
789
- cmap = hixss_friendly_chars;
790
- size = hixss_friendly_size((uint8_t*)str, cnt);
791
- break;
787
+ cmap = hixss_friendly_chars;
788
+ size = hixss_friendly_size((uint8_t *)str, cnt);
789
+ do_unicode_validation = true;
790
+ break;
792
791
  case RailsXEsc: {
793
- long sz;
794
-
795
- cmap = rails_xss_friendly_chars;
796
- sz = rails_xss_friendly_size((uint8_t*)str, cnt);
797
- if (sz < 0) {
798
- has_hi = true;
799
- size = (size_t)-sz;
800
- } else {
801
- size = (size_t)sz;
802
- }
803
- break;
792
+ long sz;
793
+
794
+ cmap = rails_xss_friendly_chars;
795
+ sz = rails_xss_friendly_size((uint8_t *)str, cnt);
796
+ if (sz < 0) {
797
+ has_hi = true;
798
+ size = (size_t)-sz;
799
+ } else {
800
+ size = (size_t)sz;
801
+ }
802
+ do_unicode_validation = true;
803
+ break;
804
+ }
805
+ case RailsEsc: {
806
+ long sz;
807
+ cmap = rails_friendly_chars;
808
+ sz = rails_friendly_size((uint8_t *)str, cnt);
809
+ if (sz < 0) {
810
+ has_hi = true;
811
+ size = (size_t)-sz;
812
+ } else {
813
+ size = (size_t)sz;
814
+ }
815
+ do_unicode_validation = true;
816
+ break;
804
817
  }
805
- case RailsEsc:
806
- cmap = rails_friendly_chars;
807
- size = rails_friendly_size((uint8_t*)str, cnt);
808
- break;
809
818
  case JSONEsc:
810
- default:
811
- cmap = hibit_friendly_chars;
812
- size = hibit_friendly_size((uint8_t*)str, cnt);
819
+ default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
813
820
  }
814
821
  assure_size(out, size + BUFFER_EXTRA);
815
822
  *out->cur++ = '"';
816
823
 
817
824
  if (escape1) {
818
- *out->cur++ = '\\';
819
- *out->cur++ = 'u';
820
- *out->cur++ = '0';
821
- *out->cur++ = '0';
822
- dump_hex((uint8_t)*str, out);
823
- cnt--;
824
- size--;
825
- str++;
826
- is_sym = 0; // just to make sure
825
+ APPEND_CHARS(out->cur, "\\u00", 4);
826
+ dump_hex((uint8_t)*str, out);
827
+ cnt--;
828
+ size--;
829
+ str++;
830
+ is_sym = 0; // just to make sure
827
831
  }
828
832
  if (cnt == size && !has_hi) {
829
- if (is_sym) {
830
- *out->cur++ = ':';
831
- }
832
- for (; '\0' != *str; str++) {
833
- *out->cur++ = *str;
834
- }
835
- *out->cur++ = '"';
833
+ if (is_sym) {
834
+ *out->cur++ = ':';
835
+ }
836
+ APPEND_CHARS(out->cur, str, cnt);
837
+ *out->cur++ = '"';
836
838
  } else {
837
- const char *end = str + cnt;
838
- const char *check_start = str;
839
-
840
- if (is_sym) {
841
- *out->cur++ = ':';
842
- }
843
- for (; str < end; str++) {
844
- switch (cmap[(uint8_t)*str]) {
845
- case '1':
846
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && check_start <= str) {
847
- if (0 != (0x80 & (uint8_t)*str)) {
848
- if (0xC0 == (0xC0 & (uint8_t)*str)) {
849
- check_start = check_unicode(str, end, orig);
850
- } else {
851
- raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
852
- }
853
- }
854
- }
855
- *out->cur++ = *str;
856
- break;
857
- case '2':
858
- *out->cur++ = '\\';
859
- switch (*str) {
860
- case '\\': *out->cur++ = '\\'; break;
861
- case '\b': *out->cur++ = 'b'; break;
862
- case '\t': *out->cur++ = 't'; break;
863
- case '\n': *out->cur++ = 'n'; break;
864
- case '\f': *out->cur++ = 'f'; break;
865
- case '\r': *out->cur++ = 'r'; break;
866
- default: *out->cur++ = *str; break;
867
- }
868
- break;
869
- case '3': // Unicode
870
- if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
871
- if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
872
- str = dump_unicode(str, end, out, orig);
873
- } else {
874
- check_start = check_unicode(str, end, orig);
875
- *out->cur++ = *str;
876
- }
877
- break;
878
- }
879
- str = dump_unicode(str, end, out, orig);
880
- break;
881
- case '6': // control characters
882
- if (*(uint8_t*)str < 0x80) {
883
- *out->cur++ = '\\';
884
- *out->cur++ = 'u';
885
- *out->cur++ = '0';
886
- *out->cur++ = '0';
887
- dump_hex((uint8_t)*str, out);
888
- } else {
889
- if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
890
- if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
891
- str = dump_unicode(str, end, out, orig);
892
- } else {
893
- check_start = check_unicode(str, end, orig);
894
- *out->cur++ = *str;
895
- }
896
- break;
897
- }
898
- str = dump_unicode(str, end, out, orig);
899
- }
900
- break;
901
- default:
902
- break; // ignore, should never happen if the table is correct
903
- }
904
- }
905
- *out->cur++ = '"';
839
+ const char *end = str + cnt;
840
+ const char *check_start = str;
841
+
842
+ if (is_sym) {
843
+ *out->cur++ = ':';
844
+ }
845
+ for (; str < end; str++) {
846
+ switch (cmap[(uint8_t)*str]) {
847
+ case '1':
848
+ if (do_unicode_validation && check_start <= str) {
849
+ if (0 != (0x80 & (uint8_t)*str)) {
850
+ if (0xC0 == (0xC0 & (uint8_t)*str)) {
851
+ check_start = check_unicode(str, end, orig);
852
+ } else {
853
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
854
+ }
855
+ }
856
+ }
857
+ *out->cur++ = *str;
858
+ break;
859
+ case '2':
860
+ *out->cur++ = '\\';
861
+ switch (*str) {
862
+ case '\\': *out->cur++ = '\\'; break;
863
+ case '\b': *out->cur++ = 'b'; break;
864
+ case '\t': *out->cur++ = 't'; break;
865
+ case '\n': *out->cur++ = 'n'; break;
866
+ case '\f': *out->cur++ = 'f'; break;
867
+ case '\r': *out->cur++ = 'r'; break;
868
+ default: *out->cur++ = *str; break;
869
+ }
870
+ break;
871
+ case '3': // Unicode
872
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
873
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
874
+ str = dump_unicode(str, end, out, orig);
875
+ } else {
876
+ check_start = check_unicode(str, end, orig);
877
+ *out->cur++ = *str;
878
+ }
879
+ break;
880
+ }
881
+ str = dump_unicode(str, end, out, orig);
882
+ break;
883
+ case '6': // control characters
884
+ if (*(uint8_t *)str < 0x80) {
885
+ if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
886
+ break;
887
+ }
888
+ APPEND_CHARS(out->cur, "\\u00", 4);
889
+ dump_hex((uint8_t)*str, out);
890
+ } else {
891
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
892
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
893
+ str = dump_unicode(str, end, out, orig);
894
+ } else {
895
+ check_start = check_unicode(str, end, orig);
896
+ *out->cur++ = *str;
897
+ }
898
+ break;
899
+ }
900
+ str = dump_unicode(str, end, out, orig);
901
+ }
902
+ break;
903
+ default: break; // ignore, should never happen if the table is correct
904
+ }
905
+ }
906
+ *out->cur++ = '"';
906
907
  }
907
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
908
- uint8_t c = (uint8_t)*(str - 1);
909
- int i;
910
- int scnt = (int)(str - orig);
911
-
912
- // Last utf-8 characters must be 0x10xxxxxx. The start must be
913
- // 0x110xxxxx for 2 characters, 0x1110xxxx for 3, and 0x11110xxx for
914
- // 4.
915
- if (0 != (0x40 & c)) {
916
- debug_raise(orig, cnt, __LINE__);
917
- }
918
- for (i = 1; i < (int)scnt && i < 4; i++) {
919
- c = str[-1 - i];
920
- if (0x80 != (0xC0 & c)) {
921
- switch (i) {
922
- case 1:
923
- if (0xC0 != (0xE0 & c)) {
924
- debug_raise(orig, cnt, __LINE__);
925
- }
926
- break;
927
- case 2:
928
- if (0xE0 != (0xF0 & c)) {
929
- debug_raise(orig, cnt, __LINE__);
930
- }
931
- break;
932
- case 3:
933
- if (0xF0 != (0xF8 & c)) {
934
- debug_raise(orig, cnt, __LINE__);
935
- }
936
- break;
937
- default: // can't get here
938
- break;
939
- }
940
- break;
941
- }
942
- }
943
- if (i == (int)scnt || 4 <= i) {
944
- debug_raise(orig, cnt, __LINE__);
945
- }
908
+ if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
909
+ uint8_t c = (uint8_t) * (str - 1);
910
+ int i;
911
+ int scnt = (int)(str - orig);
912
+
913
+ // Last utf-8 characters must be 0x10xxxxxx. The start must be
914
+ // 0x110xxxxx for 2 characters, 0x1110xxxx for 3, and 0x11110xxx for
915
+ // 4.
916
+ if (0 != (0x40 & c)) {
917
+ debug_raise(orig, cnt, __LINE__);
918
+ }
919
+ for (i = 1; i < (int)scnt && i < 4; i++) {
920
+ c = str[-1 - i];
921
+ if (0x80 != (0xC0 & c)) {
922
+ switch (i) {
923
+ case 1:
924
+ if (0xC0 != (0xE0 & c)) {
925
+ debug_raise(orig, cnt, __LINE__);
926
+ }
927
+ break;
928
+ case 2:
929
+ if (0xE0 != (0xF0 & c)) {
930
+ debug_raise(orig, cnt, __LINE__);
931
+ }
932
+ break;
933
+ case 3:
934
+ if (0xF0 != (0xF8 & c)) {
935
+ debug_raise(orig, cnt, __LINE__);
936
+ }
937
+ break;
938
+ default: // can't get here
939
+ break;
940
+ }
941
+ break;
942
+ }
943
+ }
944
+ if (i == (int)scnt || 4 <= i) {
945
+ debug_raise(orig, cnt, __LINE__);
946
+ }
946
947
  }
947
948
  *out->cur = '\0';
948
949
  }
949
950
 
950
- void
951
- oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
952
- const char *s = rb_class2name(obj);
951
+ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
952
+ const char *s = rb_class2name(obj);
953
953
 
954
954
  oj_dump_cstr(s, strlen(s), 0, 0, out);
955
955
  }
956
956
 
957
- void
958
- oj_dump_obj_to_s(VALUE obj, Out out) {
959
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
957
+ void oj_dump_obj_to_s(VALUE obj, Out out) {
958
+ volatile VALUE rstr = oj_safe_string_convert(obj);
960
959
 
961
- oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
960
+ oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
962
961
  }
963
962
 
964
- void
965
- oj_dump_raw(const char *str, size_t cnt, Out out) {
963
+ void oj_dump_raw(const char *str, size_t cnt, Out out) {
966
964
  assure_size(out, cnt + 10);
967
- memcpy(out->cur, str, cnt);
968
- out->cur += cnt;
965
+ APPEND_CHARS(out->cur, str, cnt);
969
966
  *out->cur = '\0';
970
967
  }
971
968
 
972
- void
973
- oj_grow_out(Out out, size_t len) {
974
- size_t size = out->end - out->buf;
975
- long pos = out->cur - out->buf;
976
- char *buf = out->buf;
969
+ void oj_out_init(Out out) {
970
+ out->buf = out->stack_buffer;
971
+ out->cur = out->buf;
972
+ out->end = out->buf + sizeof(out->stack_buffer) - BUFFER_EXTRA;
973
+ out->allocated = false;
974
+ }
975
+
976
+ void oj_out_free(Out out) {
977
+ if (out->allocated) {
978
+ OJ_R_FREE(out->buf); // TBD
979
+ }
980
+ }
981
+
982
+ void oj_grow_out(Out out, size_t len) {
983
+ size_t size = out->end - out->buf;
984
+ long pos = out->cur - out->buf;
985
+ char *buf = out->buf;
977
986
 
978
987
  size *= 2;
979
988
  if (size <= len * 2 + pos) {
980
- size += len;
989
+ size += len;
981
990
  }
982
991
  if (out->allocated) {
983
- REALLOC_N(buf, char, (size + BUFFER_EXTRA));
992
+ OJ_R_REALLOC_N(buf, char, (size + BUFFER_EXTRA));
984
993
  } else {
985
- buf = ALLOC_N(char, (size + BUFFER_EXTRA));
986
- out->allocated = true;
987
- memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
994
+ buf = OJ_R_ALLOC_N(char, (size + BUFFER_EXTRA));
995
+ out->allocated = true;
996
+ memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
988
997
  }
989
998
  if (0 == buf) {
990
- rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]", ENOSPC, strerror(ENOSPC));
999
+ rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]", ENOSPC, strerror(ENOSPC));
991
1000
  }
992
1001
  out->buf = buf;
993
1002
  out->end = buf + size;
994
1003
  out->cur = out->buf + pos;
995
1004
  }
996
1005
 
997
- void
998
- oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
1006
+ void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
999
1007
  assure_size(out, 4);
1000
- *out->cur++ = 'n';
1001
- *out->cur++ = 'u';
1002
- *out->cur++ = 'l';
1003
- *out->cur++ = 'l';
1008
+ APPEND_CHARS(out->cur, "null", 4);
1004
1009
  *out->cur = '\0';
1005
1010
  }
1006
1011
 
1007
- void
1008
- oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
1012
+ void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
1009
1013
  assure_size(out, 4);
1010
- *out->cur++ = 't';
1011
- *out->cur++ = 'r';
1012
- *out->cur++ = 'u';
1013
- *out->cur++ = 'e';
1014
+ APPEND_CHARS(out->cur, "true", 4);
1014
1015
  *out->cur = '\0';
1015
1016
  }
1016
1017
 
1017
- void
1018
- oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
1018
+ void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
1019
1019
  assure_size(out, 5);
1020
- *out->cur++ = 'f';
1021
- *out->cur++ = 'a';
1022
- *out->cur++ = 'l';
1023
- *out->cur++ = 's';
1024
- *out->cur++ = 'e';
1020
+ APPEND_CHARS(out->cur, "false", 5);
1025
1021
  *out->cur = '\0';
1026
1022
  }
1027
1023
 
1028
- void
1029
- oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1030
- char buf[32];
1031
- char *b = buf + sizeof(buf) - 1;
1032
- long long num = rb_num2ll(obj);
1033
- int neg = 0;
1034
- bool dump_as_string = false;
1024
+ static const char digits_table[] = "\
1025
+ 00010203040506070809\
1026
+ 10111213141516171819\
1027
+ 20212223242526272829\
1028
+ 30313233343536373839\
1029
+ 40414243444546474849\
1030
+ 50515253545556575859\
1031
+ 60616263646566676869\
1032
+ 70717273747576777879\
1033
+ 80818283848586878889\
1034
+ 90919293949596979899";
1035
+
1036
+ char *oj_longlong_to_string(long long num, bool negative, char *buf) {
1037
+ while (100 <= num) {
1038
+ unsigned idx = num % 100 * 2;
1039
+ *buf-- = digits_table[idx + 1];
1040
+ *buf-- = digits_table[idx];
1041
+ num /= 100;
1042
+ }
1043
+ if (num < 10) {
1044
+ *buf-- = num + '0';
1045
+ } else {
1046
+ *buf-- = digits_table[num * 2 + 1];
1047
+ *buf-- = digits_table[num * 2];
1048
+ }
1049
+
1050
+ if (negative) {
1051
+ *buf = '-';
1052
+ } else {
1053
+ buf++;
1054
+ }
1055
+ return buf;
1056
+ }
1057
+
1058
+ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1059
+ char buf[32];
1060
+ char *b = buf + sizeof(buf) - 1;
1061
+ long long num = NUM2LL(obj);
1062
+ bool neg = false;
1063
+ size_t cnt = 0;
1064
+ bool dump_as_string = false;
1035
1065
 
1036
1066
  if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
1037
- (out->opts->int_range_max < num || out->opts->int_range_min > num)) {
1038
- dump_as_string = true;
1067
+ (out->opts->int_range_max < num || out->opts->int_range_min > num)) {
1068
+ dump_as_string = true;
1039
1069
  }
1040
1070
  if (0 > num) {
1041
- neg = 1;
1042
- num = -num;
1071
+ neg = true;
1072
+ num = -num;
1043
1073
  }
1044
1074
  *b-- = '\0';
1045
1075
 
1046
1076
  if (dump_as_string) {
1047
- *b-- = '"';
1077
+ *b-- = '"';
1048
1078
  }
1049
1079
  if (0 < num) {
1050
- for (; 0 < num; num /= 10, b--) {
1051
- *b = (num % 10) + '0';
1052
- }
1053
- if (neg) {
1054
- *b = '-';
1055
- } else {
1056
- b++;
1057
- }
1080
+ b = oj_longlong_to_string(num, neg, b);
1058
1081
  } else {
1059
- *b = '0';
1082
+ *b = '0';
1060
1083
  }
1061
1084
  if (dump_as_string) {
1062
- *--b = '"';
1063
- }
1064
- assure_size(out, (sizeof(buf) - (b - buf)));
1065
- for (; '\0' != *b; b++) {
1066
- *out->cur++ = *b;
1085
+ *--b = '"';
1067
1086
  }
1087
+ cnt = sizeof(buf) - (b - buf) - 1;
1088
+ assure_size(out, cnt);
1089
+ APPEND_CHARS(out->cur, b, cnt);
1068
1090
  *out->cur = '\0';
1069
1091
  }
1070
1092
 
1071
- void
1072
- oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1073
- volatile VALUE rs = rb_big2str(obj, 10);
1074
- int cnt = (int)RSTRING_LEN(rs);
1075
- bool dump_as_string = false;
1093
+ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1094
+ volatile VALUE rs = rb_big2str(obj, 10);
1095
+ int cnt = (int)RSTRING_LEN(rs);
1096
+ bool dump_as_string = false;
1076
1097
 
1077
- if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1078
- dump_as_string = true;
1079
- assure_size(out, cnt + 2);
1080
- *out->cur++ = '"';
1098
+ if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1099
+ dump_as_string = true;
1100
+ assure_size(out, cnt + 2);
1101
+ *out->cur++ = '"';
1081
1102
  } else {
1082
- assure_size(out, cnt);
1103
+ assure_size(out, cnt);
1083
1104
  }
1084
- memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
1085
- out->cur += cnt;
1105
+ APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
1086
1106
  if (dump_as_string) {
1087
- *out->cur++ = '"';
1107
+ *out->cur++ = '"';
1088
1108
  }
1089
1109
  *out->cur = '\0';
1090
1110
  }
1091
1111
 
1092
1112
  // Removed dependencies on math due to problems with CentOS 5.4.
1093
- void
1094
- oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1095
- char buf[64];
1096
- char *b;
1097
- double d = rb_num2dbl(obj);
1098
- int cnt = 0;
1113
+ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1114
+ char buf[64];
1115
+ char *b;
1116
+ double d = rb_num2dbl(obj);
1117
+ int cnt = 0;
1099
1118
 
1100
1119
  if (0.0 == d) {
1101
- b = buf;
1102
- *b++ = '0';
1103
- *b++ = '.';
1104
- *b++ = '0';
1105
- *b++ = '\0';
1106
- cnt = 3;
1120
+ b = buf;
1121
+ *b++ = '0';
1122
+ *b++ = '.';
1123
+ *b++ = '0';
1124
+ *b++ = '\0';
1125
+ cnt = 3;
1107
1126
  } else if (OJ_INFINITY == d) {
1108
- if (ObjectMode == out->opts->mode) {
1109
- strcpy(buf, inf_val);
1110
- cnt = sizeof(inf_val) - 1;
1111
- } else {
1112
- NanDump nd = out->opts->dump_opts.nan_dump;
1113
-
1114
- if (AutoNan == nd) {
1115
- switch (out->opts->mode) {
1116
- case CompatMode: nd = WordNan; break;
1117
- case StrictMode: nd = RaiseNan; break;
1118
- case NullMode: nd = NullNan; break;
1119
- case CustomMode: nd = NullNan; break;
1120
- default: break;
1121
- }
1122
- }
1123
- switch (nd) {
1124
- case RaiseNan:
1125
- raise_strict(obj);
1126
- break;
1127
- case WordNan:
1128
- strcpy(buf, "Infinity");
1129
- cnt = 8;
1130
- break;
1131
- case NullNan:
1132
- strcpy(buf, "null");
1133
- cnt = 4;
1134
- break;
1135
- case HugeNan:
1136
- default:
1137
- strcpy(buf, inf_val);
1138
- cnt = sizeof(inf_val) - 1;
1139
- break;
1140
- }
1141
- }
1127
+ if (ObjectMode == out->opts->mode) {
1128
+ strcpy(buf, inf_val);
1129
+ cnt = sizeof(inf_val) - 1;
1130
+ } else {
1131
+ NanDump nd = out->opts->dump_opts.nan_dump;
1132
+
1133
+ if (AutoNan == nd) {
1134
+ switch (out->opts->mode) {
1135
+ case CompatMode: nd = WordNan; break;
1136
+ case StrictMode: nd = RaiseNan; break;
1137
+ case NullMode: nd = NullNan; break;
1138
+ case CustomMode: nd = NullNan; break;
1139
+ default: break;
1140
+ }
1141
+ }
1142
+ switch (nd) {
1143
+ case RaiseNan: raise_strict(obj); break;
1144
+ case WordNan:
1145
+ strcpy(buf, "Infinity");
1146
+ cnt = 8;
1147
+ break;
1148
+ case NullNan:
1149
+ strcpy(buf, "null");
1150
+ cnt = 4;
1151
+ break;
1152
+ case HugeNan:
1153
+ default:
1154
+ strcpy(buf, inf_val);
1155
+ cnt = sizeof(inf_val) - 1;
1156
+ break;
1157
+ }
1158
+ }
1142
1159
  } else if (-OJ_INFINITY == d) {
1143
- if (ObjectMode == out->opts->mode) {
1144
- strcpy(buf, ninf_val);
1145
- cnt = sizeof(ninf_val) - 1;
1146
- } else {
1147
- NanDump nd = out->opts->dump_opts.nan_dump;
1148
-
1149
- if (AutoNan == nd) {
1150
- switch (out->opts->mode) {
1151
- case CompatMode: nd = WordNan; break;
1152
- case StrictMode: nd = RaiseNan; break;
1153
- case NullMode: nd = NullNan; break;
1154
- default: break;
1155
- }
1156
- }
1157
- switch (nd) {
1158
- case RaiseNan:
1159
- raise_strict(obj);
1160
- break;
1161
- case WordNan:
1162
- strcpy(buf, "-Infinity");
1163
- cnt = 9;
1164
- break;
1165
- case NullNan:
1166
- strcpy(buf, "null");
1167
- cnt = 4;
1168
- break;
1169
- case HugeNan:
1170
- default:
1171
- strcpy(buf, ninf_val);
1172
- cnt = sizeof(ninf_val) - 1;
1173
- break;
1174
- }
1175
- }
1160
+ if (ObjectMode == out->opts->mode) {
1161
+ strcpy(buf, ninf_val);
1162
+ cnt = sizeof(ninf_val) - 1;
1163
+ } else {
1164
+ NanDump nd = out->opts->dump_opts.nan_dump;
1165
+
1166
+ if (AutoNan == nd) {
1167
+ switch (out->opts->mode) {
1168
+ case CompatMode: nd = WordNan; break;
1169
+ case StrictMode: nd = RaiseNan; break;
1170
+ case NullMode: nd = NullNan; break;
1171
+ default: break;
1172
+ }
1173
+ }
1174
+ switch (nd) {
1175
+ case RaiseNan: raise_strict(obj); break;
1176
+ case WordNan:
1177
+ strcpy(buf, "-Infinity");
1178
+ cnt = 9;
1179
+ break;
1180
+ case NullNan:
1181
+ strcpy(buf, "null");
1182
+ cnt = 4;
1183
+ break;
1184
+ case HugeNan:
1185
+ default:
1186
+ strcpy(buf, ninf_val);
1187
+ cnt = sizeof(ninf_val) - 1;
1188
+ break;
1189
+ }
1190
+ }
1176
1191
  } else if (isnan(d)) {
1177
- if (ObjectMode == out->opts->mode) {
1178
- strcpy(buf, nan_val);
1179
- cnt = sizeof(ninf_val) - 1;
1180
- } else {
1181
- NanDump nd = out->opts->dump_opts.nan_dump;
1182
-
1183
- if (AutoNan == nd) {
1184
- switch (out->opts->mode) {
1185
- case ObjectMode: nd = HugeNan; break;
1186
- case StrictMode: nd = RaiseNan; break;
1187
- case NullMode: nd = NullNan; break;
1188
- default: break;
1189
- }
1190
- }
1191
- switch (nd) {
1192
- case RaiseNan:
1193
- raise_strict(obj);
1194
- break;
1195
- case WordNan:
1196
- strcpy(buf, "NaN");
1197
- cnt = 3;
1198
- break;
1199
- case NullNan:
1200
- strcpy(buf, "null");
1201
- cnt = 4;
1202
- break;
1203
- case HugeNan:
1204
- default:
1205
- strcpy(buf, nan_val);
1206
- cnt = sizeof(nan_val) - 1;
1207
- break;
1208
- }
1209
- }
1192
+ if (ObjectMode == out->opts->mode) {
1193
+ strcpy(buf, nan_val);
1194
+ cnt = sizeof(nan_val) - 1;
1195
+ } else {
1196
+ NanDump nd = out->opts->dump_opts.nan_dump;
1197
+
1198
+ if (AutoNan == nd) {
1199
+ switch (out->opts->mode) {
1200
+ case ObjectMode: nd = HugeNan; break;
1201
+ case StrictMode: nd = RaiseNan; break;
1202
+ case NullMode: nd = NullNan; break;
1203
+ default: break;
1204
+ }
1205
+ }
1206
+ switch (nd) {
1207
+ case RaiseNan: raise_strict(obj); break;
1208
+ case WordNan:
1209
+ strcpy(buf, "NaN");
1210
+ cnt = 3;
1211
+ break;
1212
+ case NullNan:
1213
+ strcpy(buf, "null");
1214
+ cnt = 4;
1215
+ break;
1216
+ case HugeNan:
1217
+ default:
1218
+ strcpy(buf, nan_val);
1219
+ cnt = sizeof(nan_val) - 1;
1220
+ break;
1221
+ }
1222
+ }
1210
1223
  } else if (d == (double)(long long int)d) {
1211
- cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1224
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1212
1225
  } else if (0 == out->opts->float_prec) {
1213
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1214
-
1215
- cnt = (int)RSTRING_LEN(rstr);
1216
- if ((int)sizeof(buf) <= cnt) {
1217
- cnt = sizeof(buf) - 1;
1218
- }
1219
- strncpy(buf, rb_string_value_ptr((VALUE*)&rstr), cnt);
1220
- buf[cnt] = '\0';
1226
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1227
+
1228
+ cnt = (int)RSTRING_LEN(rstr);
1229
+ if ((int)sizeof(buf) <= cnt) {
1230
+ cnt = sizeof(buf) - 1;
1231
+ }
1232
+ memcpy(buf, RSTRING_PTR(rstr), cnt);
1233
+ buf[cnt] = '\0';
1221
1234
  } else {
1222
- cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
1235
+ cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
1223
1236
  }
1224
1237
  assure_size(out, cnt);
1225
- for (b = buf; '\0' != *b; b++) {
1226
- *out->cur++ = *b;
1227
- }
1238
+ APPEND_CHARS(out->cur, buf, cnt);
1228
1239
  *out->cur = '\0';
1229
1240
  }
1230
1241
 
1231
- int
1232
- oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
1233
- int cnt = snprintf(buf, blen, format, d);
1242
+ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
1243
+ int cnt = snprintf(buf, blen, format, d);
1234
1244
 
1235
1245
  // Round off issues at 16 significant digits so check for obvious ones of
1236
1246
  // 0001 and 9999.
1237
1247
  if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
1238
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1248
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1239
1249
 
1240
- strcpy(buf, rb_string_value_ptr((VALUE*)&rstr));
1241
- cnt = (int)RSTRING_LEN(rstr);
1250
+ strcpy(buf, RSTRING_PTR(rstr));
1251
+ cnt = (int)RSTRING_LEN(rstr);
1242
1252
  }
1243
1253
  return cnt;
1244
1254
  }
1245
-
1246
- bool
1247
- oj_dump_ignore(Options opts, VALUE obj) {
1248
- if (NULL != opts->ignore && (ObjectMode == opts->mode || CustomMode == opts->mode)) {
1249
- VALUE *vp = opts->ignore;
1250
- VALUE clas = rb_obj_class(obj);
1251
-
1252
- for (; Qnil != *vp; vp++) {
1253
- if (clas == *vp) {
1254
- return true;
1255
- }
1256
- }
1257
- }
1258
- return false;
1259
- }