oj 3.7.12

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