oj 3.10.7

Sign up to get free protection for your applications and to get access to all the features.
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
+ }