oj 3.7.12

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