oj 3.10.7

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