oj 3.7.4 → 3.13.21

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