oj 3.7.4 → 3.13.21

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 (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
- }