oj 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  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 +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
data/ext/oj/dump.c CHANGED
@@ -1,31 +1,6 @@
1
1
  /* dump.c
2
- * Copyright (c) 2012, Peter Ohler
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
3
  * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
4
  */
30
5
 
31
6
  #include <stdlib.h>
@@ -37,91 +12,43 @@
37
12
  #include <stdio.h>
38
13
  #include <string.h>
39
14
  #include <math.h>
15
+ #include <unistd.h>
16
+ #include <errno.h>
40
17
 
41
18
  #include "oj.h"
42
19
  #include "cache8.h"
20
+ #include "dump.h"
21
+ #include "odd.h"
43
22
 
44
- #if !HAS_ENCODING_SUPPORT || defined(RUBINIUS_RUBY)
45
- #define rb_eEncodingError rb_eException
46
- #endif
23
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
24
+ #define OJ_INFINITY (1.0/0.0)
47
25
 
48
- //Workaround:
49
- #ifndef INFINITY
50
- #define INFINITY (1.0/0.0)
51
- #endif
52
-
26
+ #define MAX_DEPTH 1000
53
27
 
54
- typedef unsigned long ulong;
28
+ static const char inf_val[] = INF_VAL;
29
+ static const char ninf_val[] = NINF_VAL;
30
+ static const char nan_val[] = NAN_VAL;
55
31
 
56
- typedef struct _Out {
57
- char *buf;
58
- char *end;
59
- char *cur;
60
- Cache8 circ_cache;
61
- slot_t circ_cnt;
62
- int indent;
63
- int depth; // used by dump_hash
64
- Options opts;
65
- uint32_t hash_cnt;
66
- } *Out;
67
-
68
- static void dump_obj_to_json(VALUE obj, Options copts, Out out);
69
- static void raise_strict(VALUE obj);
70
- static void dump_val(VALUE obj, int depth, Out out);
71
- static void dump_nil(Out out);
72
- static void dump_true(Out out);
73
- static void dump_false(Out out);
74
- static void dump_fixnum(VALUE obj, Out out);
75
- static void dump_bignum(VALUE obj, Out out);
76
- static void dump_float(VALUE obj, Out out);
77
- static void dump_raw(const char *str, size_t cnt, Out out);
78
- static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
79
- static void dump_hex(uint8_t c, Out out);
80
- static void dump_str_comp(VALUE obj, Out out);
81
- static void dump_str_obj(VALUE obj, Out out);
82
- static void dump_sym_comp(VALUE obj, Out out);
83
- static void dump_sym_obj(VALUE obj, Out out);
84
- static void dump_class_comp(VALUE obj, Out out);
85
- static void dump_class_obj(VALUE obj, Out out);
86
- static void dump_array(VALUE obj, int depth, Out out);
87
- static int hash_cb_strict(VALUE key, VALUE value, Out out);
88
- static int hash_cb_compat(VALUE key, VALUE value, Out out);
89
- static int hash_cb_object(VALUE key, VALUE value, Out out);
90
- static void dump_hash(VALUE obj, int depth, int mode, Out out);
91
- static void dump_time(VALUE obj, Out out);
92
- static void dump_ruby_time(VALUE obj, Out out);
93
- static void dump_xml_time(VALUE obj, Out out);
94
- static void dump_data_strict(VALUE obj, Out out);
95
- static void dump_data_null(VALUE obj, Out out);
96
- static void dump_data_comp(VALUE obj, Out out);
97
- static void dump_data_obj(VALUE obj, int depth, Out out);
98
- static void dump_obj_comp(VALUE obj, int depth, Out out);
99
- static void dump_obj_obj(VALUE obj, int depth, Out out);
100
- #if HAS_RSTRUCT
101
- static void dump_struct_comp(VALUE obj, int depth, Out out);
102
- static void dump_struct_obj(VALUE obj, int depth, Out out);
103
- #endif
104
- #if HAS_IVAR_HELPERS
105
- static int dump_attr_cb(ID key, VALUE value, Out out);
106
- #endif
107
- static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
108
- static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out);
32
+ typedef unsigned long ulong;
109
33
 
110
- static void grow(Out out, size_t len);
111
34
  static size_t hibit_friendly_size(const uint8_t *str, size_t len);
35
+ static size_t xss_friendly_size(const uint8_t *str, size_t len);
112
36
  static size_t ascii_friendly_size(const uint8_t *str, size_t len);
113
37
 
114
- static void dump_leaf_to_json(Leaf leaf, Options copts, Out out);
115
- static void dump_leaf(Leaf leaf, int depth, Out out);
116
- static void dump_leaf_str(Leaf leaf, Out out);
117
- static void dump_leaf_fixnum(Leaf leaf, Out out);
118
- static void dump_leaf_float(Leaf leaf, Out out);
119
- static void dump_leaf_array(Leaf leaf, int depth, Out out);
120
- static void dump_leaf_hash(Leaf leaf, int depth, Out out);
121
-
122
-
123
38
  static const char hex_chars[17] = "0123456789abcdef";
124
39
 
40
+ // JSON standard except newlines are no escaped
41
+ static char newline_friendly_chars[256] = "\
42
+ 66666666221622666666666666666666\
43
+ 11211111111111111111111111111111\
44
+ 11111111111111111111111111112111\
45
+ 11111111111111111111111111111111\
46
+ 11111111111111111111111111111111\
47
+ 11111111111111111111111111111111\
48
+ 11111111111111111111111111111111\
49
+ 11111111111111111111111111111111";
50
+
51
+ // JSON standard
125
52
  static char hibit_friendly_chars[256] = "\
126
53
  66666666222622666666666666666666\
127
54
  11211111111111111111111111111111\
@@ -136,7 +63,18 @@ static char hibit_friendly_chars[256] = "\
136
63
  // bytes per character in the output. That makes this conservative.
137
64
  static char ascii_friendly_chars[256] = "\
138
65
  66666666222622666666666666666666\
139
- 11211111111111121111111111111111\
66
+ 11211111111111111111111111111111\
67
+ 11111111111111111111111111112111\
68
+ 11111111111111111111111111111116\
69
+ 33333333333333333333333333333333\
70
+ 33333333333333333333333333333333\
71
+ 33333333333333333333333333333333\
72
+ 33333333333333333333333333333333";
73
+
74
+ // XSS safe mode
75
+ static char xss_friendly_chars[256] = "\
76
+ 66666666222622666666666666666666\
77
+ 11211161111111121111111111116161\
140
78
  11111111111111111111111111112111\
141
79
  11111111111111111111111111111116\
142
80
  33333333333333333333333333333333\
@@ -144,11 +82,50 @@ static char ascii_friendly_chars[256] = "\
144
82
  33333333333333333333333333333333\
145
83
  33333333333333333333333333333333";
146
84
 
85
+ // JSON XSS combo
86
+ static char hixss_friendly_chars[256] = "\
87
+ 66666666222622666666666666666666\
88
+ 11211161111111111111111111116161\
89
+ 11111111111111111111111111112111\
90
+ 11111111111111111111111111111111\
91
+ 11111111111111111111111111111111\
92
+ 11111111111111111111111111111111\
93
+ 11111111111111111111111111111111\
94
+ 11311111111111111111111111111111";
95
+
96
+ // Rails HTML non-escape
97
+ static char rails_friendly_chars[256] = "\
98
+ 66666666222622666666666666666666\
99
+ 11211111111111111111111111111111\
100
+ 11111111111111111111111111112111\
101
+ 11111111111111111111111111111111\
102
+ 11111111111111111111111111111111\
103
+ 11111111111111111111111111111111\
104
+ 11111111111111111111111111111111\
105
+ 11311111111111111111111111111111";
106
+
107
+ static void
108
+ raise_strict(VALUE obj) {
109
+ rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.", rb_class2name(rb_obj_class(obj)));
110
+ }
111
+
112
+ inline static size_t
113
+ newline_friendly_size(const uint8_t *str, size_t len) {
114
+ size_t size = 0;
115
+ size_t i = len;
116
+
117
+ for (; 0 < i; str++, i--) {
118
+ size += newline_friendly_chars[*str];
119
+ }
120
+ return size - len * (size_t)'0';
121
+ }
122
+
147
123
  inline static size_t
148
124
  hibit_friendly_size(const uint8_t *str, size_t len) {
149
125
  size_t size = 0;
126
+ size_t i = len;
150
127
 
151
- for (; 0 < len; str++, len--) {
128
+ for (; 0 < i; str++, i--) {
152
129
  size += hibit_friendly_chars[*str];
153
130
  }
154
131
  return size - len * (size_t)'0';
@@ -157,74 +134,87 @@ hibit_friendly_size(const uint8_t *str, size_t len) {
157
134
  inline static size_t
158
135
  ascii_friendly_size(const uint8_t *str, size_t len) {
159
136
  size_t size = 0;
137
+ size_t i = len;
160
138
 
161
- for (; 0 < len; str++, len--) {
139
+ for (; 0 < i; str++, i--) {
162
140
  size += ascii_friendly_chars[*str];
163
141
  }
164
142
  return size - len * (size_t)'0';
165
143
  }
166
144
 
167
- inline static void
168
- fill_indent(Out out, int cnt) {
169
- if (0 < cnt && 0 < out->indent) {
170
- cnt *= out->indent;
171
- *out->cur++ = '\n';
172
- for (; 0 < cnt; cnt--) {
173
- *out->cur++ = ' ';
174
- }
145
+ inline static size_t
146
+ xss_friendly_size(const uint8_t *str, size_t len) {
147
+ size_t size = 0;
148
+ size_t i = len;
149
+
150
+ for (; 0 < i; str++, i--) {
151
+ size += xss_friendly_chars[*str];
175
152
  }
153
+ return size - len * (size_t)'0';
176
154
  }
177
155
 
178
- inline static const char*
179
- ulong2str(uint32_t num, char *end) {
180
- char *b;
156
+ inline static size_t
157
+ hixss_friendly_size(const uint8_t *str, size_t len) {
158
+ size_t size = 0;
159
+ size_t i = len;
181
160
 
182
- *end-- = '\0';
183
- for (b = end; 0 < num || b == end; num /= 10, b--) {
184
- *b = (num % 10) + '0';
161
+ for (; 0 < i; str++, i--) {
162
+ size += hixss_friendly_chars[*str];
185
163
  }
186
- b++;
187
-
188
- return b;
164
+ return size - len * (size_t)'0';
189
165
  }
190
166
 
191
- inline static void
192
- dump_ulong(unsigned long num, Out out) {
193
- char buf[32];
194
- char *b = buf + sizeof(buf) - 1;
167
+ inline static size_t
168
+ rails_friendly_size(const uint8_t *str, size_t len) {
169
+ size_t size = 0;
170
+ size_t i = len;
195
171
 
196
- *b-- = '\0';
197
- if (0 < num) {
198
- for (; 0 < num; num /= 10, b--) {
199
- *b = (num % 10) + '0';
200
- }
201
- b++;
202
- } else {
203
- *b = '0';
172
+ for (; 0 < i; str++, i--) {
173
+ size += rails_friendly_chars[*str];
204
174
  }
205
- for (; '\0' != *b; b++) {
206
- *out->cur++ = *b;
207
- }
208
- *out->cur = '\0';
175
+ return size - len * (size_t)'0';
209
176
  }
210
177
 
211
- static void
212
- grow(Out out, size_t len) {
213
- size_t size = out->end - out->buf;
214
- long pos = out->cur - out->buf;
215
- char *buf;
216
-
217
- size *= 2;
218
- if (size <= len * 2 + pos) {
219
- size += len;
178
+ const char*
179
+ oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
180
+ const char *str = NULL;
181
+
182
+ if (AutoNan == opt) {
183
+ switch (mode) {
184
+ case CompatMode: opt = WordNan; break;
185
+ case StrictMode: opt = RaiseNan; break;
186
+ default: break;
187
+ }
220
188
  }
221
- buf = REALLOC_N(out->buf, char, (size + 10));
222
- if (0 == buf) { // 1 extra for terminator character plus extra (paranoid)
223
- rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]\n", ENOSPC, strerror(ENOSPC));
189
+ switch (opt) {
190
+ case RaiseNan:
191
+ raise_strict(obj);
192
+ break;
193
+ case WordNan:
194
+ if (plus) {
195
+ str = "Infinity";
196
+ *lenp = 8;
197
+ } else {
198
+ str = "-Infinity";
199
+ *lenp = 9;
200
+ }
201
+ break;
202
+ case NullNan:
203
+ str = "null";
204
+ *lenp = 4;
205
+ break;
206
+ case HugeNan:
207
+ default:
208
+ if (plus) {
209
+ str = inf_val;
210
+ *lenp = sizeof(inf_val) - 1;
211
+ } else {
212
+ str = ninf_val;
213
+ *lenp = sizeof(ninf_val) - 1;
214
+ }
215
+ break;
224
216
  }
225
- out->buf = buf;
226
- out->end = buf + size;
227
- out->cur = out->buf + pos;
217
+ return str;
228
218
  }
229
219
 
230
220
  inline static void
@@ -236,17 +226,7 @@ dump_hex(uint8_t c, Out out) {
236
226
  *out->cur++ = hex_chars[d];
237
227
  }
238
228
 
239
- static void
240
- dump_raw(const char *str, size_t cnt, Out out) {
241
- if (out->end - out->cur <= (long)cnt + 10) {
242
- grow(out, cnt + 10);
243
- }
244
- memcpy(out->cur, str, cnt);
245
- out->cur += cnt;
246
- *out->cur = '\0';
247
- }
248
-
249
- const char*
229
+ static const char*
250
230
  dump_unicode(const char *str, const char *end, Out out) {
251
231
  uint32_t code = 0;
252
232
  uint8_t b = *(uint8_t*)str;
@@ -268,13 +248,14 @@ dump_unicode(const char *str, const char *end, Out out) {
268
248
  cnt = 5;
269
249
  code = b & 0x00000001;
270
250
  } else {
271
- rb_raise(rb_eEncodingError, "Invalid Unicode\n");
251
+ cnt = 0;
252
+ rb_raise(oj_json_generator_error_class, "Invalid Unicode");
272
253
  }
273
254
  str++;
274
255
  for (; 0 < cnt; cnt--, str++) {
275
256
  b = *(uint8_t*)str;
276
257
  if (end <= str || 0x80 != (0xC0 & b)) {
277
- rb_raise(rb_eEncodingError, "Invalid Unicode\n");
258
+ rb_raise(oj_json_generator_error_class, "Invalid Unicode");
278
259
  }
279
260
  code = (code << 6) | (b & 0x0000003F);
280
261
  }
@@ -298,174 +279,412 @@ dump_unicode(const char *str, const char *end, Out out) {
298
279
  return str - 1;
299
280
  }
300
281
 
301
- // returns 0 if not using circular references, -1 if not further writing is
302
- // needed (duplicate), and a positive value if the object was added to the cache.
303
- static long
304
- check_circular(VALUE obj, Out out) {
282
+ // Returns 0 if not using circular references, -1 if no further writing is
283
+ // needed (duplicate), and a positive value if the object was added to the
284
+ // cache.
285
+ long
286
+ oj_check_circular(VALUE obj, Out out) {
305
287
  slot_t id = 0;
306
288
  slot_t *slot;
307
289
 
308
- if (ObjectMode == out->opts->mode && Yes == out->opts->circular) {
290
+ if (Yes == out->opts->circular) {
309
291
  if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
310
292
  out->circ_cnt++;
311
293
  id = out->circ_cnt;
312
294
  *slot = id;
313
295
  } else {
314
- if (out->end - out->cur <= 18) {
315
- grow(out, 18);
296
+ if (ObjectMode == out->opts->mode) {
297
+ assure_size(out, 18);
298
+ *out->cur++ = '"';
299
+ *out->cur++ = '^';
300
+ *out->cur++ = 'r';
301
+ dump_ulong(id, out);
302
+ *out->cur++ = '"';
316
303
  }
317
- *out->cur++ = '"';
318
- *out->cur++ = '^';
319
- *out->cur++ = 'r';
320
- dump_ulong(id, out);
321
- *out->cur++ = '"';
322
-
323
304
  return -1;
324
305
  }
325
306
  }
326
307
  return (long)id;
327
308
  }
328
309
 
329
- static void
330
- dump_nil(Out out) {
331
- size_t size = 4;
310
+ void
311
+ oj_dump_time(VALUE obj, Out out, int withZone) {
312
+ char buf[64];
313
+ char *b = buf + sizeof(buf) - 1;
314
+ long size;
315
+ char *dot;
316
+ int neg = 0;
317
+ long one = 1000000000;
318
+ #if HAS_RB_TIME_TIMESPEC
319
+ struct timespec ts = rb_time_timespec(obj);
320
+ time_t sec = ts.tv_sec;
321
+ long nsec = ts.tv_nsec;
322
+ #else
323
+ time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
324
+ #if HAS_NANO_TIME
325
+ long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
326
+ #else
327
+ long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
328
+ #endif
329
+ #endif
330
+
331
+ *b-- = '\0';
332
+ if (withZone) {
333
+ long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
334
+ int zneg = (0 > tzsecs);
332
335
 
333
- if (out->end - out->cur <= (long)size) {
334
- grow(out, size);
336
+ if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
337
+ tzsecs = 86400;
338
+ }
339
+ if (zneg) {
340
+ tzsecs = -tzsecs;
341
+ }
342
+ if (0 == tzsecs) {
343
+ *b-- = '0';
344
+ } else {
345
+ for (; 0 < tzsecs; b--, tzsecs /= 10) {
346
+ *b = '0' + (tzsecs % 10);
347
+ }
348
+ if (zneg) {
349
+ *b-- = '-';
350
+ }
351
+ }
352
+ *b-- = 'e';
335
353
  }
336
- *out->cur++ = 'n';
337
- *out->cur++ = 'u';
338
- *out->cur++ = 'l';
339
- *out->cur++ = 'l';
340
- *out->cur = '\0';
341
- }
342
-
343
- static void
344
- dump_true(Out out) {
345
- size_t size = 4;
354
+ if (0 > sec) {
355
+ neg = 1;
356
+ sec = -sec;
357
+ if (0 < nsec) {
358
+ nsec = 1000000000 - nsec;
359
+ sec--;
360
+ }
361
+ }
362
+ dot = b - 9;
363
+ if (0 < out->opts->sec_prec) {
364
+ if (9 > out->opts->sec_prec) {
365
+ int i;
346
366
 
347
- if (out->end - out->cur <= (long)size) {
348
- grow(out, size);
367
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
368
+ dot++;
369
+ nsec = (nsec + 5) / 10;
370
+ one /= 10;
371
+ }
372
+ }
373
+ if (one <= nsec) {
374
+ nsec -= one;
375
+ sec++;
376
+ }
377
+ for (; dot < b; b--, nsec /= 10) {
378
+ *b = '0' + (nsec % 10);
379
+ }
380
+ *b-- = '.';
349
381
  }
350
- *out->cur++ = 't';
351
- *out->cur++ = 'r';
352
- *out->cur++ = 'u';
353
- *out->cur++ = 'e';
382
+ if (0 == sec) {
383
+ *b-- = '0';
384
+ } else {
385
+ for (; 0 < sec; b--, sec /= 10) {
386
+ *b = '0' + (sec % 10);
387
+ }
388
+ }
389
+ if (neg) {
390
+ *b-- = '-';
391
+ }
392
+ b++;
393
+ size = sizeof(buf) - (b - buf) - 1;
394
+ assure_size(out, size);
395
+ memcpy(out->cur, b, size);
396
+ out->cur += size;
354
397
  *out->cur = '\0';
355
398
  }
356
399
 
357
- static void
358
- dump_false(Out out) {
359
- size_t size = 5;
400
+ void
401
+ oj_dump_ruby_time(VALUE obj, Out out) {
402
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
360
403
 
361
- if (out->end - out->cur <= (long)size) {
362
- grow(out, size);
363
- }
364
- *out->cur++ = 'f';
365
- *out->cur++ = 'a';
366
- *out->cur++ = 'l';
367
- *out->cur++ = 's';
368
- *out->cur++ = 'e';
369
- *out->cur = '\0';
404
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
370
405
  }
371
406
 
372
- static void
373
- dump_fixnum(VALUE obj, Out out) {
374
- char buf[32];
375
- char *b = buf + sizeof(buf) - 1;
376
- long num = NUM2LONG(obj);
377
- int neg = 0;
407
+ void
408
+ oj_dump_xml_time(VALUE obj, Out out) {
409
+ char buf[64];
410
+ struct tm *tm;
411
+ long one = 1000000000;
412
+ #if HAS_RB_TIME_TIMESPEC
413
+ struct timespec ts = rb_time_timespec(obj);
414
+ time_t sec = ts.tv_sec;
415
+ long nsec = ts.tv_nsec;
416
+ #else
417
+ time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
418
+ #if HAS_NANO_TIME
419
+ long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
420
+ #else
421
+ long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
422
+ #endif
423
+ #endif
424
+ long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
425
+ int tzhour, tzmin;
426
+ char tzsign = '+';
378
427
 
379
- if (0 > num) {
380
- neg = 1;
381
- num = -num;
382
- }
383
- *b-- = '\0';
384
- if (0 < num) {
385
- for (; 0 < num; num /= 10, b--) {
386
- *b = (num % 10) + '0';
387
- }
388
- if (neg) {
389
- *b = '-';
428
+ assure_size(out, 36);
429
+ if (9 > out->opts->sec_prec) {
430
+ int i;
431
+
432
+ // This is pretty lame but to be compatible with rails and active
433
+ // support rounding is not done but instead a floor is done when
434
+ // second precision is 3 just to be like rails. sigh.
435
+ if (3 == out->opts->sec_prec) {
436
+ nsec /= 1000000;
437
+ one = 1000;
390
438
  } else {
391
- b++;
439
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
440
+ nsec = (nsec + 5) / 10;
441
+ one /= 10;
442
+ }
443
+ if (one <= nsec) {
444
+ nsec -= one;
445
+ sec++;
446
+ }
392
447
  }
393
- } else {
394
- *b = '0';
395
448
  }
396
- if (out->end - out->cur <= (long)(sizeof(buf) - (b - buf))) {
397
- grow(out, sizeof(buf) - (b - buf));
449
+ // 2012-01-05T23:58:07.123456000+09:00
450
+ //tm = localtime(&sec);
451
+ sec += tzsecs;
452
+ tm = gmtime(&sec);
453
+ #if 1
454
+ if (0 > tzsecs) {
455
+ tzsign = '-';
456
+ tzhour = (int)(tzsecs / -3600);
457
+ tzmin = (int)(tzsecs / -60) - (tzhour * 60);
458
+ } else {
459
+ tzhour = (int)(tzsecs / 3600);
460
+ tzmin = (int)(tzsecs / 60) - (tzhour * 60);
398
461
  }
399
- for (; '\0' != *b; b++) {
400
- *out->cur++ = *b;
462
+ #else
463
+ if (0 > tm->tm_gmtoff) {
464
+ tzsign = '-';
465
+ tzhour = (int)(tm->tm_gmtoff / -3600);
466
+ tzmin = (int)(tm->tm_gmtoff / -60) - (tzhour * 60);
467
+ } else {
468
+ tzhour = (int)(tm->tm_gmtoff / 3600);
469
+ tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
401
470
  }
402
- *out->cur = '\0';
403
- }
471
+ #endif
472
+ if (0 == nsec || 0 == out->opts->sec_prec) {
473
+ if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
474
+ sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
475
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
476
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
477
+ oj_dump_cstr(buf, 20, 0, 0, out);
478
+ } else {
479
+ sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
480
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
481
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
482
+ tzsign, tzhour, tzmin);
483
+ oj_dump_cstr(buf, 25, 0, 0, out);
484
+ }
485
+ } else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
486
+ char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
487
+ int len = 30;
404
488
 
405
- static void
406
- dump_bignum(VALUE obj, Out out) {
407
- VALUE rs = rb_big2str(obj, 10);
408
- int cnt = (int)RSTRING_LEN(rs);
489
+ if (9 > out->opts->sec_prec) {
490
+ format[32] = '0' + out->opts->sec_prec;
491
+ len -= 9 - out->opts->sec_prec;
492
+ }
493
+ sprintf(buf, format,
494
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
495
+ tm->tm_hour, tm->tm_min, tm->tm_sec, nsec);
496
+ oj_dump_cstr(buf, len, 0, 0, out);
497
+ } else {
498
+ char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
499
+ int len = 35;
409
500
 
410
- if (out->end - out->cur <= (long)cnt) {
411
- grow(out, cnt);
501
+ if (9 > out->opts->sec_prec) {
502
+ format[32] = '0' + out->opts->sec_prec;
503
+ len -= 9 - out->opts->sec_prec;
504
+ }
505
+ sprintf(buf, format,
506
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
507
+ tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
508
+ tzsign, tzhour, tzmin);
509
+ oj_dump_cstr(buf, len, 0, 0, out);
412
510
  }
413
- memcpy(out->cur, StringValuePtr(rs), cnt);
414
- out->cur += cnt;
415
- *out->cur = '\0';
416
511
  }
417
512
 
418
- // Removed dependencies on math due to problems with CentOS 5.4.
419
- static void
420
- dump_float(VALUE obj, Out out) {
421
- char buf[64];
422
- char *b;
423
- double d = rb_num2dbl(obj);
424
- int cnt;
513
+ void
514
+ oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
515
+ oj_dump_obj_to_json_using_params(obj, copts, out, 0, 0);
516
+ }
425
517
 
426
- if (0.0 == d) {
427
- b = buf;
428
- *b++ = '0';
429
- *b++ = '.';
430
- *b++ = '0';
431
- *b++ = '\0';
432
- cnt = 3;
433
- } else if (INFINITY == d) {
434
- strcpy(buf, "Infinity");
435
- cnt = 8;
436
- } else if (-INFINITY == d) {
437
- strcpy(buf, "-Infinity");
438
- cnt = 9;
439
- } else if (d == (double)(long long int)d) {
440
- cnt = sprintf(buf, "%.1f", d); // used sprintf due to bug in snprintf
441
- } else {
442
- cnt = sprintf(buf, "%0.16g", d); // used sprintf due to bug in snprintf
518
+ void
519
+ oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
520
+ if (0 == out->buf) {
521
+ out->buf = ALLOC_N(char, 4096);
522
+ out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
523
+ out->allocated = 1;
443
524
  }
444
- if (out->end - out->cur <= (long)cnt) {
445
- grow(out, cnt);
525
+ out->cur = out->buf;
526
+ out->circ_cnt = 0;
527
+ out->opts = copts;
528
+ out->hash_cnt = 0;
529
+ out->indent = copts->indent;
530
+ out->argc = argc;
531
+ out->argv = argv;
532
+ if (Yes == copts->circular) {
533
+ oj_cache8_new(&out->circ_cache);
446
534
  }
447
- for (b = buf; '\0' != *b; b++) {
448
- *out->cur++ = *b;
535
+ switch (copts->mode) {
536
+ case StrictMode: oj_dump_strict_val(obj, 0, out); break;
537
+ case NullMode: oj_dump_null_val(obj, 0, out); break;
538
+ case ObjectMode: oj_dump_obj_val(obj, 0, out); break;
539
+ case CompatMode: oj_dump_compat_val(obj, 0, out, Yes == copts->to_json); break;
540
+ case RailsMode: oj_dump_rails_val(obj, 0, out, true); break;
541
+ case CustomMode: oj_dump_custom_val(obj, 0, out, true); break;
542
+ default: oj_dump_custom_val(obj, 0, out, true); break;
543
+ }
544
+ if (0 < out->indent) {
545
+ switch (*(out->cur - 1)) {
546
+ case ']':
547
+ case '}':
548
+ assure_size(out, 1);
549
+ *out->cur++ = '\n';
550
+ default:
551
+ break;
552
+ }
449
553
  }
450
554
  *out->cur = '\0';
555
+ if (Yes == copts->circular) {
556
+ oj_cache8_delete(out->circ_cache);
557
+ }
451
558
  }
452
559
 
453
- static void
454
- dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
560
+ void
561
+ oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
562
+ char buf[4096];
563
+ struct _Out out;
564
+ size_t size;
565
+ FILE *f;
566
+ int ok;
567
+
568
+ out.buf = buf;
569
+ out.end = buf + sizeof(buf) - BUFFER_EXTRA;
570
+ out.allocated = 0;
571
+ out.omit_nil = copts->dump_opts.omit_nil;
572
+ oj_dump_obj_to_json(obj, copts, &out);
573
+ size = out.cur - out.buf;
574
+ if (0 == (f = fopen(path, "w"))) {
575
+ if (out.allocated) {
576
+ xfree(out.buf);
577
+ }
578
+ rb_raise(rb_eIOError, "%s", strerror(errno));
579
+ }
580
+ ok = (size == fwrite(out.buf, 1, size, f));
581
+ if (out.allocated) {
582
+ xfree(out.buf);
583
+ }
584
+ fclose(f);
585
+ if (!ok) {
586
+ int err = ferror(f);
587
+
588
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
589
+ }
590
+ }
591
+
592
+ void
593
+ oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
594
+ char buf[4096];
595
+ struct _Out out;
596
+ ssize_t size;
597
+ VALUE clas = rb_obj_class(stream);
598
+ #if !IS_WINDOWS
599
+ int fd;
600
+ VALUE s;
601
+ #endif
602
+
603
+ out.buf = buf;
604
+ out.end = buf + sizeof(buf) - BUFFER_EXTRA;
605
+ out.allocated = 0;
606
+ out.omit_nil = copts->dump_opts.omit_nil;
607
+ oj_dump_obj_to_json(obj, copts, &out);
608
+ size = out.cur - out.buf;
609
+ if (oj_stringio_class == clas) {
610
+ rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
611
+ #if !IS_WINDOWS
612
+ } else if (rb_respond_to(stream, oj_fileno_id) &&
613
+ Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
614
+ 0 != (fd = FIX2INT(s))) {
615
+ if (size != write(fd, out.buf, size)) {
616
+ if (out.allocated) {
617
+ xfree(out.buf);
618
+ }
619
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]", errno, strerror(errno));
620
+ }
621
+ #endif
622
+ } else if (rb_respond_to(stream, oj_write_id)) {
623
+ rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
624
+ } else {
625
+ if (out.allocated) {
626
+ xfree(out.buf);
627
+ }
628
+ rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
629
+ }
630
+ if (out.allocated) {
631
+ xfree(out.buf);
632
+ }
633
+ }
634
+
635
+ void
636
+ oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
637
+ #if HAS_ENCODING_SUPPORT
638
+ rb_encoding *enc = rb_to_encoding(rb_obj_encoding(obj));
639
+
640
+ if (rb_utf8_encoding() != enc) {
641
+ obj = rb_str_conv_enc(obj, enc, rb_utf8_encoding());
642
+ }
643
+ #endif
644
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
645
+ }
646
+
647
+ void
648
+ oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
649
+ const char *sym = rb_id2name(SYM2ID(obj));
650
+
651
+ oj_dump_cstr(sym, strlen(sym), 0, 0, out);
652
+ }
653
+
654
+ void
655
+ oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
455
656
  size_t size;
456
657
  char *cmap;
457
658
 
458
- if (Yes == out->opts->ascii_only) {
659
+ switch (out->opts->escape_mode) {
660
+ case NLEsc:
661
+ cmap = newline_friendly_chars;
662
+ size = newline_friendly_size((uint8_t*)str, cnt);
663
+ break;
664
+ case ASCIIEsc:
459
665
  cmap = ascii_friendly_chars;
460
666
  size = ascii_friendly_size((uint8_t*)str, cnt);
461
- } else {
667
+ break;
668
+ case XSSEsc:
669
+ cmap = xss_friendly_chars;
670
+ size = xss_friendly_size((uint8_t*)str, cnt);
671
+ break;
672
+ case JXEsc:
673
+ cmap = hixss_friendly_chars;
674
+ size = hixss_friendly_size((uint8_t*)str, cnt);
675
+ break;
676
+ case RailsEsc:
677
+ cmap = rails_friendly_chars;
678
+ size = rails_friendly_size((uint8_t*)str, cnt);
679
+ break;
680
+ case JSONEsc:
681
+ default:
462
682
  cmap = hibit_friendly_chars;
463
683
  size = hibit_friendly_size((uint8_t*)str, cnt);
464
684
  }
465
- if (out->end - out->cur <= (long)size + 10) { // extra 10 for escaped first char, quotes, and sym
466
- grow(out, size + 10);
467
- }
685
+ assure_size(out, size + BUFFER_EXTRA);
468
686
  *out->cur++ = '"';
687
+
469
688
  if (escape1) {
470
689
  *out->cur++ = '\\';
471
690
  *out->cur++ = 'u';
@@ -499,6 +718,7 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
499
718
  case '2':
500
719
  *out->cur++ = '\\';
501
720
  switch (*str) {
721
+ case '\\': *out->cur++ = '\\'; break;
502
722
  case '\b': *out->cur++ = 'b'; break;
503
723
  case '\t': *out->cur++ = 't'; break;
504
724
  case '\n': *out->cur++ = 'n'; break;
@@ -508,6 +728,14 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
508
728
  }
509
729
  break;
510
730
  case '3': // Unicode
731
+ if (0xe2 == (uint8_t)*str && JXEsc == out->opts->escape_mode && 2 <= end - str) {
732
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
733
+ str = dump_unicode(str, end, out);
734
+ } else {
735
+ *out->cur++ = *str;
736
+ }
737
+ break;
738
+ }
511
739
  str = dump_unicode(str, end, out);
512
740
  break;
513
741
  case '6': // control characters
@@ -521,1301 +749,305 @@ dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
521
749
  break; // ignore, should never happen if the table is correct
522
750
  }
523
751
  }
524
- *out->cur++ = '"';
752
+ *out->cur++ = '"';
753
+ }
754
+ if (JXEsc == out->opts->escape_mode && 0 != (0x80 & *(str - 1))) {
755
+ uint8_t c = (uint8_t)*(str - 1);
756
+ int i;
757
+
758
+ // Last utf-8 characters must be 0x10xxxxxx. The start must be
759
+ // 0x110xxxxx for 2 characters, 0x1110xxxx for 3, and 0x11110xxx for
760
+ // 4.
761
+ if (0 != (0x40 & c)) {
762
+ rb_raise(oj_json_generator_error_class, "Partial character in string. 1");
763
+ }
764
+ for (i = 1; i < (int)cnt && i < 4; i++) {
765
+ c = str[-1 - i];
766
+ if (0x80 != (0xC0 & c)) {
767
+ switch (i) {
768
+ case 1:
769
+ if (0xC0 != (0xE0 & c)) {
770
+ rb_raise(oj_json_generator_error_class, "Partial character in string.");
771
+ }
772
+ break;
773
+ case 2:
774
+ if (0xE0 != (0xF0 & c)) {
775
+ rb_raise(oj_json_generator_error_class, "Partial character in string. 2");
776
+ }
777
+ break;
778
+ case 3:
779
+ if (0xF0 != (0xF8 & c)) {
780
+ rb_raise(oj_json_generator_error_class, "Partial character in string.");
781
+ }
782
+ break;
783
+ default: // can't get here
784
+ break;
785
+ }
786
+ break;
787
+ }
788
+ }
789
+ if (i == (int)cnt || 4 <= i) {
790
+ rb_raise(oj_json_generator_error_class, "Partial character in string.");
791
+ }
525
792
  }
526
793
  *out->cur = '\0';
527
794
  }
528
795
 
529
- static void
530
- dump_str_comp(VALUE obj, Out out) {
531
- dump_cstr(StringValuePtr(obj), RSTRING_LEN(obj), 0, 0, out);
796
+ void
797
+ oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
798
+ const char *s = rb_class2name(obj);
799
+
800
+ oj_dump_cstr(s, strlen(s), 0, 0, out);
532
801
  }
533
802
 
534
- static void
535
- dump_str_obj(VALUE obj, Out out) {
536
- const char *s = StringValuePtr(obj);
537
- size_t len = RSTRING_LEN(obj);
538
- char s1 = s[1];
803
+ void
804
+ oj_dump_obj_to_s(VALUE obj, Out out) {
805
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
539
806
 
540
- dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
807
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
541
808
  }
542
809
 
543
- static void
544
- dump_sym_comp(VALUE obj, Out out) {
545
- const char *sym = rb_id2name(SYM2ID(obj));
546
-
547
- dump_cstr(sym, strlen(sym), 0, 0, out);
810
+ void
811
+ oj_dump_raw(const char *str, size_t cnt, Out out) {
812
+ assure_size(out, cnt + 10);
813
+ memcpy(out->cur, str, cnt);
814
+ out->cur += cnt;
815
+ *out->cur = '\0';
548
816
  }
549
817
 
550
- static void
551
- dump_sym_obj(VALUE obj, Out out) {
552
- const char *sym = rb_id2name(SYM2ID(obj));
553
- size_t len = strlen(sym);
554
-
555
- dump_cstr(sym, len, 1, 0, out);
818
+ void
819
+ oj_grow_out(Out out, size_t len) {
820
+ size_t size = out->end - out->buf;
821
+ long pos = out->cur - out->buf;
822
+ char *buf;
823
+
824
+ size *= 2;
825
+ if (size <= len * 2 + pos) {
826
+ size += len;
827
+ }
828
+ if (out->allocated) {
829
+ buf = REALLOC_N(out->buf, char, (size + BUFFER_EXTRA));
830
+ } else {
831
+ buf = ALLOC_N(char, (size + BUFFER_EXTRA));
832
+ out->allocated = 1;
833
+ memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
834
+ }
835
+ if (0 == buf) {
836
+ rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]", ENOSPC, strerror(ENOSPC));
837
+ }
838
+ out->buf = buf;
839
+ out->end = buf + size;
840
+ out->cur = out->buf + pos;
556
841
  }
557
842
 
558
- static void
559
- dump_class_comp(VALUE obj, Out out) {
560
- const char *s = rb_class2name(obj);
561
-
562
- dump_cstr(s, strlen(s), 0, 0, out);
843
+ void
844
+ oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
845
+ assure_size(out, 4);
846
+ *out->cur++ = 'n';
847
+ *out->cur++ = 'u';
848
+ *out->cur++ = 'l';
849
+ *out->cur++ = 'l';
850
+ *out->cur = '\0';
563
851
  }
564
852
 
565
- static void
566
- dump_class_obj(VALUE obj, Out out) {
567
- const char *s = rb_class2name(obj);
568
- size_t len = strlen(s);
853
+ void
854
+ oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
855
+ assure_size(out, 4);
856
+ *out->cur++ = 't';
857
+ *out->cur++ = 'r';
858
+ *out->cur++ = 'u';
859
+ *out->cur++ = 'e';
860
+ *out->cur = '\0';
861
+ }
569
862
 
570
- if (out->end - out->cur <= 6) {
571
- grow(out, 6);
572
- }
573
- *out->cur++ = '{';
574
- *out->cur++ = '"';
575
- *out->cur++ = '^';
576
- *out->cur++ = 'c';
577
- *out->cur++ = '"';
578
- *out->cur++ = ':';
579
- dump_cstr(s, len, 0, 0, out);
580
- *out->cur++ = '}';
863
+ void
864
+ oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
865
+ assure_size(out, 5);
866
+ *out->cur++ = 'f';
867
+ *out->cur++ = 'a';
868
+ *out->cur++ = 'l';
869
+ *out->cur++ = 's';
870
+ *out->cur++ = 'e';
581
871
  *out->cur = '\0';
582
872
  }
583
873
 
584
- static void
585
- dump_array(VALUE a, int depth, Out out) {
586
- VALUE *np;
587
- size_t size;
588
- int cnt;
589
- int d2 = depth + 1;
590
- long id = check_circular(a, out);
874
+ void
875
+ oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
876
+ char buf[32];
877
+ char *b = buf + sizeof(buf) - 1;
878
+ long long num = rb_num2ll(obj);
879
+ int neg = 0;
591
880
 
592
- if (id < 0) {
593
- return;
881
+ if (0 > num) {
882
+ neg = 1;
883
+ num = -num;
594
884
  }
595
- np = RARRAY_PTR(a);
596
- cnt = (int)RARRAY_LEN(a);
597
- *out->cur++ = '[';
598
- if (0 < id) {
599
- size = d2 * out->indent + 16;
600
- if (out->end - out->cur <= (long)size) {
601
- grow(out, size);
885
+ *b-- = '\0';
886
+ if (0 < num) {
887
+ for (; 0 < num; num /= 10, b--) {
888
+ *b = (num % 10) + '0';
602
889
  }
603
- fill_indent(out, d2);
604
- *out->cur++ = '"';
605
- *out->cur++ = '^';
606
- *out->cur++ = 'i';
607
- dump_ulong(id, out);
608
- *out->cur++ = '"';
890
+ if (neg) {
891
+ *b = '-';
892
+ } else {
893
+ b++;
894
+ }
895
+ } else {
896
+ *b = '0';
609
897
  }
610
- size = 2;
611
- if (out->end - out->cur <= (long)size) {
612
- grow(out, size);
898
+ assure_size(out, (sizeof(buf) - (b - buf)));
899
+ for (; '\0' != *b; b++) {
900
+ *out->cur++ = *b;
613
901
  }
614
- if (0 == cnt) {
615
- *out->cur++ = ']';
616
- } else {
617
- if (0 < id) {
618
- *out->cur++ = ',';
619
- }
620
- if (0 == out->opts->dump_opts) {
621
- size = d2 * out->indent + 2;
902
+ *out->cur = '\0';
903
+ }
904
+
905
+ void
906
+ oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
907
+ volatile VALUE rs = rb_big2str(obj, 10);
908
+ int cnt = (int)RSTRING_LEN(rs);
909
+
910
+ assure_size(out, cnt);
911
+ memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
912
+ out->cur += cnt;
913
+ *out->cur = '\0';
914
+ }
915
+
916
+ // Removed dependencies on math due to problems with CentOS 5.4.
917
+ void
918
+ oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
919
+ char buf[64];
920
+ char *b;
921
+ double d = rb_num2dbl(obj);
922
+ int cnt = 0;
923
+
924
+ if (0.0 == d) {
925
+ b = buf;
926
+ *b++ = '0';
927
+ *b++ = '.';
928
+ *b++ = '0';
929
+ *b++ = '\0';
930
+ cnt = 3;
931
+ } else if (OJ_INFINITY == d) {
932
+ if (ObjectMode == out->opts->mode) {
933
+ strcpy(buf, inf_val);
934
+ cnt = sizeof(inf_val) - 1;
622
935
  } else {
623
- size = d2 * out->opts->dump_opts->indent_size + out->opts->dump_opts->array_size + 1;
624
- }
625
- for (; 0 < cnt; cnt--, np++) {
626
- if (out->end - out->cur <= (long)size) {
627
- grow(out, size);
628
- }
629
- if (0 == out->opts->dump_opts) {
630
- fill_indent(out, d2);
631
- } else {
632
- if (0 < out->opts->dump_opts->array_size) {
633
- strcpy(out->cur, out->opts->dump_opts->array_nl);
634
- out->cur += out->opts->dump_opts->array_size;
635
- }
636
- if (0 < out->opts->dump_opts->indent_size) {
637
- int i;
638
- for (i = d2; 0 < i; i--) {
639
- strcpy(out->cur, out->opts->dump_opts->indent);
640
- out->cur += out->opts->dump_opts->indent_size;
641
- }
936
+ NanDump nd = out->opts->dump_opts.nan_dump;
937
+
938
+ if (AutoNan == nd) {
939
+ switch (out->opts->mode) {
940
+ case CompatMode: nd = WordNan; break;
941
+ case StrictMode: nd = RaiseNan; break;
942
+ case NullMode: nd = NullNan; break;
943
+ case CustomMode: nd = NullNan; break;
944
+ default: break;
642
945
  }
643
946
  }
644
- dump_val(*np, d2, out);
645
- if (1 < cnt) {
646
- *out->cur++ = ',';
947
+ switch (nd) {
948
+ case RaiseNan:
949
+ raise_strict(obj);
950
+ break;
951
+ case WordNan:
952
+ strcpy(buf, "Infinity");
953
+ cnt = 8;
954
+ break;
955
+ case NullNan:
956
+ strcpy(buf, "null");
957
+ cnt = 4;
958
+ break;
959
+ case HugeNan:
960
+ default:
961
+ strcpy(buf, inf_val);
962
+ cnt = sizeof(inf_val) - 1;
963
+ break;
647
964
  }
648
965
  }
649
- size = depth * out->indent + 1;
650
- if (out->end - out->cur <= (long)size) {
651
- grow(out, size);
652
- }
653
- if (0 == out->opts->dump_opts) {
654
- fill_indent(out, depth);
966
+ } else if (-OJ_INFINITY == d) {
967
+ if (ObjectMode == out->opts->mode) {
968
+ strcpy(buf, ninf_val);
969
+ cnt = sizeof(ninf_val) - 1;
655
970
  } else {
656
- //printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
657
- if (0 < out->opts->dump_opts->array_size) {
658
- strcpy(out->cur, out->opts->dump_opts->array_nl);
659
- out->cur += out->opts->dump_opts->array_size;
660
- }
661
- if (0 < out->opts->dump_opts->indent_size) {
662
- int i;
663
-
664
- for (i = depth; 0 < i; i--) {
665
- strcpy(out->cur, out->opts->dump_opts->indent);
666
- out->cur += out->opts->dump_opts->indent_size;
971
+ NanDump nd = out->opts->dump_opts.nan_dump;
972
+
973
+ if (AutoNan == nd) {
974
+ switch (out->opts->mode) {
975
+ case CompatMode: nd = WordNan; break;
976
+ case StrictMode: nd = RaiseNan; break;
977
+ case NullMode: nd = NullNan; break;
978
+ default: break;
667
979
  }
668
980
  }
669
- }
670
- *out->cur++ = ']';
671
- }
672
- *out->cur = '\0';
673
- }
674
-
675
- static int
676
- hash_cb_strict(VALUE key, VALUE value, Out out) {
677
- int depth = out->depth;
678
- long size;
679
-
680
- if (rb_type(key) != T_STRING) {
681
- rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings, not %s.\n", rb_class2name(rb_obj_class(key)));
682
- }
683
- if (0 == out->opts->dump_opts) {
684
- size = depth * out->indent + 1;
685
- if (out->end - out->cur <= size) {
686
- grow(out, size);
687
- }
688
- fill_indent(out, depth);
689
- dump_str_comp(key, out);
690
- *out->cur++ = ':';
691
- } else {
692
- size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
693
- if (out->end - out->cur <= size) {
694
- grow(out, size);
695
- }
696
- if (0 < out->opts->dump_opts->hash_size) {
697
- strcpy(out->cur, out->opts->dump_opts->hash_nl);
698
- out->cur += out->opts->dump_opts->hash_size;
699
- }
700
- if (0 < out->opts->dump_opts->indent_size) {
701
- int i;
702
- for (i = depth; 0 < i; i--) {
703
- strcpy(out->cur, out->opts->dump_opts->indent);
704
- out->cur += out->opts->dump_opts->indent_size;
981
+ switch (nd) {
982
+ case RaiseNan:
983
+ raise_strict(obj);
984
+ break;
985
+ case WordNan:
986
+ strcpy(buf, "-Infinity");
987
+ cnt = 9;
988
+ break;
989
+ case NullNan:
990
+ strcpy(buf, "null");
991
+ cnt = 4;
992
+ break;
993
+ case HugeNan:
994
+ default:
995
+ strcpy(buf, ninf_val);
996
+ cnt = sizeof(ninf_val) - 1;
997
+ break;
705
998
  }
706
999
  }
707
- dump_str_comp(key, out);
708
- size = out->opts->dump_opts->before_size + out->opts->dump_opts->after_size + 2;
709
- if (out->end - out->cur <= size) {
710
- grow(out, size);
711
- }
712
- if (0 < out->opts->dump_opts->before_size) {
713
- strcpy(out->cur, out->opts->dump_opts->before_sep);
714
- out->cur += out->opts->dump_opts->before_size;
715
- }
716
- *out->cur++ = ':';
717
- if (0 < out->opts->dump_opts->after_size) {
718
- strcpy(out->cur, out->opts->dump_opts->after_sep);
719
- out->cur += out->opts->dump_opts->after_size;
1000
+ } else if (isnan(d)) {
1001
+ if (ObjectMode == out->opts->mode) {
1002
+ strcpy(buf, nan_val);
1003
+ cnt = sizeof(ninf_val) - 1;
1004
+ } else {
1005
+ NanDump nd = out->opts->dump_opts.nan_dump;
1006
+
1007
+ if (AutoNan == nd) {
1008
+ switch (out->opts->mode) {
1009
+ case ObjectMode: nd = HugeNan; break;
1010
+ case StrictMode: nd = RaiseNan; break;
1011
+ case NullMode: nd = NullNan; break;
1012
+ default: break;
1013
+ }
1014
+ }
1015
+ switch (nd) {
1016
+ case RaiseNan:
1017
+ raise_strict(obj);
1018
+ break;
1019
+ case WordNan:
1020
+ strcpy(buf, "NaN");
1021
+ cnt = 3;
1022
+ break;
1023
+ case NullNan:
1024
+ strcpy(buf, "null");
1025
+ cnt = 4;
1026
+ break;
1027
+ case HugeNan:
1028
+ default:
1029
+ strcpy(buf, nan_val);
1030
+ cnt = sizeof(nan_val) - 1;
1031
+ break;
1032
+ }
720
1033
  }
721
- }
722
- dump_val(value, depth, out);
723
- out->depth = depth;
724
- *out->cur++ = ',';
725
-
726
- return ST_CONTINUE;
727
- }
728
-
729
- static int
730
- hash_cb_compat(VALUE key, VALUE value, Out out) {
731
- int depth = out->depth;
732
- long size;
1034
+ } else if (d == (double)(long long int)d) {
1035
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1036
+ } else if (0 == out->opts->float_prec) {
1037
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
733
1038
 
734
- if (0 == out->opts->dump_opts) {
735
- size = depth * out->indent + 1;
736
- if (out->end - out->cur <= size) {
737
- grow(out, size);
1039
+ cnt = RSTRING_LEN(rstr);
1040
+ if ((int)sizeof(buf) <= cnt) {
1041
+ cnt = sizeof(buf) - 1;
738
1042
  }
739
- fill_indent(out, depth);
1043
+ strncpy(buf, rb_string_value_ptr((VALUE*)&rstr), cnt);
1044
+ buf[cnt] = '\0';
740
1045
  } else {
741
- size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
742
- if (out->end - out->cur <= size) {
743
- grow(out, size);
744
- }
745
- if (0 < out->opts->dump_opts->hash_size) {
746
- strcpy(out->cur, out->opts->dump_opts->hash_nl);
747
- out->cur += out->opts->dump_opts->hash_size;
748
- }
749
- if (0 < out->opts->dump_opts->indent_size) {
750
- int i;
751
- for (i = depth; 0 < i; i--) {
752
- strcpy(out->cur, out->opts->dump_opts->indent);
753
- out->cur += out->opts->dump_opts->indent_size;
754
- }
755
- }
756
- }
757
- switch (rb_type(key)) {
758
- case T_STRING:
759
- dump_str_comp(key, out);
760
- break;
761
- case T_SYMBOL:
762
- dump_sym_comp(key, out);
763
- break;
764
- default:
765
- /*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
766
- dump_str_comp(rb_funcall(key, oj_to_s_id, 0), out);
767
- break;
1046
+ cnt = snprintf(buf, sizeof(buf), out->opts->float_fmt, d);
768
1047
  }
769
- if (0 == out->opts->dump_opts) {
770
- *out->cur++ = ':';
771
- } else {
772
- size = out->opts->dump_opts->before_size + out->opts->dump_opts->after_size + 2;
773
- if (out->end - out->cur <= size) {
774
- grow(out, size);
775
- }
776
- if (0 < out->opts->dump_opts->before_size) {
777
- strcpy(out->cur, out->opts->dump_opts->before_sep);
778
- out->cur += out->opts->dump_opts->before_size;
779
- }
780
- *out->cur++ = ':';
781
- if (0 < out->opts->dump_opts->after_size) {
782
- strcpy(out->cur, out->opts->dump_opts->after_sep);
783
- out->cur += out->opts->dump_opts->after_size;
784
- }
785
- }
786
- dump_val(value, depth, out);
787
- out->depth = depth;
788
- *out->cur++ = ',';
789
-
790
- return ST_CONTINUE;
791
- }
792
-
793
- static int
794
- hash_cb_object(VALUE key, VALUE value, Out out) {
795
- int depth = out->depth;
796
- long size = depth * out->indent + 1;
797
-
798
- if (out->end - out->cur <= size) {
799
- grow(out, size);
800
- }
801
- fill_indent(out, depth);
802
- if (rb_type(key) == T_STRING) {
803
- dump_str_obj(key, out);
804
- *out->cur++ = ':';
805
- dump_val(value, depth, out);
806
- } else if (rb_type(key) == T_SYMBOL) {
807
- dump_sym_obj(key, out);
808
- *out->cur++ = ':';
809
- dump_val(value, depth, out);
810
- } else {
811
- int d2 = depth + 1;
812
- long s2 = size + out->indent + 1;
813
- int i;
814
- int started = 0;
815
- uint8_t b;
816
-
817
- if (out->end - out->cur <= s2 + 15) {
818
- grow(out, s2 + 15);
819
- }
820
- *out->cur++ = '"';
821
- *out->cur++ = '^';
822
- *out->cur++ = '#';
823
- out->hash_cnt++;
824
- for (i = 28; 0 <= i; i -= 4) {
825
- b = (uint8_t)((out->hash_cnt >> i) & 0x0000000F);
826
- if ('\0' != b) {
827
- started = 1;
828
- }
829
- if (started) {
830
- *out->cur++ = hex_chars[b];
831
- }
832
- }
833
- *out->cur++ = '"';
834
- *out->cur++ = ':';
835
- *out->cur++ = '[';
836
- fill_indent(out, d2);
837
- dump_val(key, d2, out);
838
- if (out->end - out->cur <= (long)s2) {
839
- grow(out, s2);
840
- }
841
- *out->cur++ = ',';
842
- fill_indent(out, d2);
843
- dump_val(value, d2, out);
844
- if (out->end - out->cur <= (long)size) {
845
- grow(out, size);
846
- }
847
- fill_indent(out, depth);
848
- *out->cur++ = ']';
849
- }
850
- out->depth = depth;
851
- *out->cur++ = ',';
852
-
853
- return ST_CONTINUE;
854
- }
855
-
856
- static void
857
- dump_hash(VALUE obj, int depth, int mode, Out out) {
858
- int cnt = (int)RHASH_SIZE(obj);
859
- size_t size = depth * out->indent + 2;
860
-
861
- if (out->end - out->cur <= 2) {
862
- grow(out, 2);
863
- }
864
- if (0 == cnt) {
865
- *out->cur++ = '{';
866
- *out->cur++ = '}';
867
- } else {
868
- long id = check_circular(obj, out);
869
-
870
- if (0 > id) {
871
- return;
872
- }
873
- *out->cur++ = '{';
874
- if (0 < id) {
875
- if (out->end - out->cur <= (long)size + 16) {
876
- grow(out, size + 16);
877
- }
878
- fill_indent(out, depth + 1);
879
- *out->cur++ = '"';
880
- *out->cur++ = '^';
881
- *out->cur++ = 'i';
882
- *out->cur++ = '"';
883
- *out->cur++ = ':';
884
- dump_ulong(id, out);
885
- *out->cur++ = ',';
886
- }
887
- out->depth = depth + 1;
888
- if (ObjectMode == mode) {
889
- rb_hash_foreach(obj, hash_cb_object, (VALUE)out);
890
- } else if (CompatMode == mode) {
891
- rb_hash_foreach(obj, hash_cb_compat, (VALUE)out);
892
- } else {
893
- rb_hash_foreach(obj, hash_cb_strict, (VALUE)out);
894
- }
895
- if (',' == *(out->cur - 1)) {
896
- out->cur--; // backup to overwrite last comma
897
- }
898
- if (0 == out->opts->dump_opts) {
899
- if (out->end - out->cur <= (long)size) {
900
- grow(out, size);
901
- }
902
- fill_indent(out, depth);
903
- } else {
904
- size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
905
- if (out->end - out->cur <= (long)size) {
906
- grow(out, size);
907
- }
908
- if (0 < out->opts->dump_opts->hash_size) {
909
- strcpy(out->cur, out->opts->dump_opts->hash_nl);
910
- out->cur += out->opts->dump_opts->hash_size;
911
- }
912
- if (0 < out->opts->dump_opts->indent_size) {
913
- int i;
914
- for (i = depth; 0 < i; i--) {
915
- strcpy(out->cur, out->opts->dump_opts->indent);
916
- out->cur += out->opts->dump_opts->indent_size;
917
- }
918
- }
919
- }
920
- *out->cur++ = '}';
921
- }
922
- *out->cur = '\0';
923
- }
924
-
925
- static void
926
- dump_time(VALUE obj, Out out) {
927
- char buf[64];
928
- char *b = buf + sizeof(buf) - 1;
929
- long size;
930
- char *dot = b - 10;
931
- #if HAS_RB_TIME_TIMESPEC
932
- struct timespec ts = rb_time_timespec(obj);
933
- time_t sec = ts.tv_sec;
934
- long nsec = ts.tv_nsec;
935
- #else
936
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
937
- #if HAS_NANO_TIME
938
- long nsec = NUM2LONG(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
939
- #else
940
- long nsec = NUM2LONG(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
941
- #endif
942
- #endif
943
-
944
- *b-- = '\0';
945
- for (; dot < b; b--, nsec /= 10) {
946
- *b = '0' + (nsec % 10);
947
- }
948
- *b-- = '.';
949
- for (; 0 < sec; b--, sec /= 10) {
950
- *b = '0' + (sec % 10);
951
- }
952
- b++;
953
- size = sizeof(buf) - (b - buf) - 1;
954
- if (out->end - out->cur <= size) {
955
- grow(out, size);
956
- }
957
- memcpy(out->cur, b, size);
958
- out->cur += size;
959
- *out->cur = '\0';
960
- }
961
-
962
- static void
963
- dump_ruby_time(VALUE obj, Out out) {
964
- VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
965
-
966
- dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
967
- }
968
-
969
- static void
970
- dump_xml_time(VALUE obj, Out out) {
971
- char buf[64];
972
- struct tm *tm;
973
- #if HAS_RB_TIME_TIMESPEC
974
- struct timespec ts = rb_time_timespec(obj);
975
- time_t sec = ts.tv_sec;
976
- long nsec = ts.tv_nsec;
977
- #else
978
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
979
- #if HAS_NANO_TIME
980
- long nsec = NUM2LONG(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
981
- #else
982
- long nsec = NUM2LONG(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
983
- #endif
984
- #endif
985
- long tz_secs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
986
- int tzhour, tzmin;
987
- char tzsign = '+';
988
-
989
- if (out->end - out->cur <= 36) {
990
- grow(out, 36);
991
- }
992
- // 2012-01-05T23:58:07.123456000+09:00
993
- //tm = localtime(&sec);
994
- sec += tz_secs;
995
- tm = gmtime(&sec);
996
- #if 1
997
- if (0 > tz_secs) {
998
- tzsign = '-';
999
- tzhour = (int)(tz_secs / -3600);
1000
- tzmin = (int)(tz_secs / -60) - (tzhour * 60);
1001
- } else {
1002
- tzhour = (int)(tz_secs / 3600);
1003
- tzmin = (int)(tz_secs / 60) - (tzhour * 60);
1004
- }
1005
- #else
1006
- if (0 > tm->tm_gmtoff) {
1007
- tzsign = '-';
1008
- tzhour = (int)(tm->tm_gmtoff / -3600);
1009
- tzmin = (int)(tm->tm_gmtoff / -60) - (tzhour * 60);
1010
- } else {
1011
- tzhour = (int)(tm->tm_gmtoff / 3600);
1012
- tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
1013
- }
1014
- #endif
1015
- if (0 == nsec) {
1016
- if (0 == tzhour && 0 == tzmin) {
1017
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
1018
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1019
- tm->tm_hour, tm->tm_min, tm->tm_sec);
1020
- dump_cstr(buf, 20, 0, 0, out);
1021
- } else {
1022
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
1023
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1024
- tm->tm_hour, tm->tm_min, tm->tm_sec,
1025
- tzsign, tzhour, tzmin);
1026
- dump_cstr(buf, 25, 0, 0, out);
1027
- }
1028
- } else {
1029
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d",
1030
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1031
- tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
1032
- tzsign, tzhour, tzmin);
1033
- dump_cstr(buf, 35, 0, 0, out);
1034
- }
1035
- }
1036
-
1037
- static void
1038
- dump_data_strict(VALUE obj, Out out) {
1039
- VALUE clas = rb_obj_class(obj);
1040
-
1041
- if (oj_bigdecimal_class == clas) {
1042
- VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1043
-
1044
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1045
- } else {
1046
- raise_strict(obj);
1047
- }
1048
- }
1049
-
1050
- static void
1051
- dump_data_null(VALUE obj, Out out) {
1052
- VALUE clas = rb_obj_class(obj);
1053
-
1054
- if (oj_bigdecimal_class == clas) {
1055
- VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1056
-
1057
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1058
- } else {
1059
- dump_nil(out);
1060
- }
1061
- }
1062
-
1063
- static void
1064
- dump_data_comp(VALUE obj, Out out) {
1065
- VALUE clas = rb_obj_class(obj);
1066
-
1067
- if (rb_cTime == clas) {
1068
- switch (out->opts->time_format) {
1069
- case RubyTime: dump_ruby_time(obj, out); break;
1070
- case XmlTime: dump_xml_time(obj, out); break;
1071
- case UnixTime:
1072
- default: dump_time(obj, out); break;
1073
- }
1074
- } else {
1075
- VALUE rstr;
1076
-
1077
- if (oj_bigdecimal_class == clas) {
1078
- //rstr = rb_funcall(obj, oj_to_s_id, 1, rb_intern("E"));
1079
- rstr = rb_funcall(obj, oj_to_s_id, 0);
1080
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1081
- } else {
1082
- rstr = rb_funcall(obj, oj_to_s_id, 0);
1083
- dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1084
- }
1085
- }
1086
- }
1087
-
1088
- static void
1089
- dump_data_obj(VALUE obj, int depth, Out out) {
1090
- VALUE clas = rb_obj_class(obj);
1091
-
1092
- if (rb_cTime == clas) {
1093
- if (out->end - out->cur <= 6) {
1094
- grow(out, 6);
1095
- }
1096
- *out->cur++ = '{';
1097
- *out->cur++ = '"';
1098
- *out->cur++ = '^';
1099
- *out->cur++ = 't';
1100
- *out->cur++ = '"';
1101
- *out->cur++ = ':';
1102
- dump_time(obj, out);
1103
- *out->cur++ = '}';
1104
- *out->cur = '\0';
1105
- } else {
1106
- Odd odd = oj_get_odd(clas);
1107
-
1108
- if (0 == odd) {
1109
- if (oj_bigdecimal_class == clas) {
1110
- VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1111
-
1112
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1113
- } else {
1114
- dump_nil(out);
1115
- }
1116
- } else {
1117
- dump_odd(obj, odd, clas, depth + 1, out);
1118
- }
1119
- }
1120
- }
1121
-
1122
- static void
1123
- dump_obj_comp(VALUE obj, int depth, Out out) {
1124
- if (rb_respond_to(obj, oj_to_hash_id)) {
1125
- VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1126
-
1127
- if (T_HASH != rb_type(h)) {
1128
- rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1129
- }
1130
- dump_hash(h, depth, out->opts->mode, out);
1131
- } else if (rb_respond_to(obj, oj_as_json_id)) {
1132
- dump_val(rb_funcall(obj, oj_as_json_id, 0), depth, out);
1133
- } else if (rb_respond_to(obj, oj_to_json_id)) {
1134
- VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
1135
- const char *s = StringValuePtr(rs);
1136
- int len = (int)RSTRING_LEN(rs);
1137
-
1138
- if (out->end - out->cur <= len) {
1139
- grow(out, len);
1140
- }
1141
- memcpy(out->cur, s, len);
1142
- out->cur += len;
1143
- } else {
1144
- VALUE clas = rb_obj_class(obj);
1145
-
1146
- if (oj_bigdecimal_class == clas) {
1147
- VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1148
-
1149
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1150
- } else {
1151
- Odd odd = oj_get_odd(clas);
1152
-
1153
- if (0 == odd) {
1154
- dump_obj_attrs(obj, 0, 0, depth, out);
1155
- } else {
1156
- dump_odd(obj, odd, 0, depth + 1, out);
1157
- }
1158
- }
1159
- }
1160
- *out->cur = '\0';
1161
- }
1162
-
1163
- inline static void
1164
- dump_obj_obj(VALUE obj, int depth, Out out) {
1165
- long id = check_circular(obj, out);
1166
-
1167
- if (0 <= id) {
1168
- VALUE clas = rb_obj_class(obj);
1169
- Odd odd = oj_get_odd(clas);
1170
-
1171
- if (0 == odd) {
1172
- if (oj_bigdecimal_class == clas) {
1173
- VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1174
-
1175
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1176
- } else {
1177
- dump_obj_attrs(obj, clas, id, depth, out);
1178
- }
1179
- } else {
1180
- dump_odd(obj, odd, clas, depth + 1, out);
1181
- }
1182
- }
1183
- }
1184
-
1185
- #if HAS_IVAR_HELPERS
1186
- static int
1187
- dump_attr_cb(ID key, VALUE value, Out out) {
1188
- int depth = out->depth;
1189
- size_t size = depth * out->indent + 1;
1190
- const char *attr = rb_id2name(key);
1191
-
1192
- if (out->end - out->cur <= (long)size) {
1193
- grow(out, size);
1194
- }
1195
- fill_indent(out, depth);
1196
- if ('@' == *attr) {
1197
- attr++;
1198
- dump_cstr(attr, strlen(attr), 0, 0, out);
1199
- } else {
1200
- char buf[32];
1201
-
1202
- *buf = '~';
1203
- strncpy(buf + 1, attr, sizeof(buf) - 2);
1204
- buf[sizeof(buf) - 1] = '\0';
1205
- dump_cstr(buf, strlen(buf), 0, 0, out);
1206
- }
1207
- *out->cur++ = ':';
1208
- dump_val(value, depth, out);
1209
- out->depth = depth;
1210
- *out->cur++ = ',';
1211
-
1212
- return ST_CONTINUE;
1213
- }
1214
- #endif
1215
-
1216
- static void
1217
- dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1218
- size_t size;
1219
- int d2 = depth + 1;
1220
-
1221
- if (out->end - out->cur <= 2) {
1222
- grow(out, 2);
1223
- }
1224
- *out->cur++ = '{';
1225
- if (0 != clas) {
1226
- const char *class_name = rb_class2name(clas);
1227
- int clen = (int)strlen(class_name);
1228
-
1229
- size = d2 * out->indent + clen + 10;
1230
- if (out->end - out->cur <= (long)size) {
1231
- grow(out, size);
1232
- }
1233
- fill_indent(out, d2);
1234
- *out->cur++ = '"';
1235
- *out->cur++ = '^';
1236
- *out->cur++ = 'o';
1237
- *out->cur++ = '"';
1238
- *out->cur++ = ':';
1239
- dump_cstr(class_name, clen, 0, 0, out);
1240
- }
1241
- if (0 < id) {
1242
- size = d2 * out->indent + 16;
1243
- if (out->end - out->cur <= (long)size) {
1244
- grow(out, size);
1245
- }
1246
- *out->cur++ = ',';
1247
- fill_indent(out, d2);
1248
- *out->cur++ = '"';
1249
- *out->cur++ = '^';
1250
- *out->cur++ = 'i';
1251
- *out->cur++ = '"';
1252
- *out->cur++ = ':';
1253
- dump_ulong(id, out);
1254
- }
1255
- {
1256
- int cnt;
1257
- // use encoding as the indicator for Ruby 1.8.7 or 1.9.x
1258
- #if HAS_IVAR_HELPERS
1259
- cnt = (int)rb_ivar_count(obj);
1260
- #else
1261
- VALUE vars = rb_funcall2(obj, oj_instance_variables_id, 0, 0);
1262
- VALUE *np = RARRAY_PTR(vars);
1263
- ID vid;
1264
- const char *attr;
1265
- int i;
1266
-
1267
- cnt = (int)RARRAY_LEN(vars);
1268
- #endif
1269
- if (0 != clas && 0 < cnt) {
1270
- *out->cur++ = ',';
1271
- }
1272
- out->depth = depth + 1;
1273
- #if HAS_IVAR_HELPERS
1274
- rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
1275
- if (',' == *(out->cur - 1)) {
1276
- out->cur--; // backup to overwrite last comma
1277
- }
1278
- #else
1279
- size = d2 * out->indent + 1;
1280
- #if HAS_EXCEPTION_MAGIC
1281
- if (Qtrue == rb_obj_is_kind_of(obj, rb_eException)) {
1282
- if (',' != *(out->cur - 1)) {
1283
- *out->cur++ = ',';
1284
- }
1285
- // message
1286
- if (out->end - out->cur <= (long)size) {
1287
- grow(out, size);
1288
- }
1289
- fill_indent(out, d2);
1290
- dump_cstr("~mesg", 5, 0, 0, out);
1291
- *out->cur++ = ':';
1292
- dump_val(rb_funcall2(obj, rb_intern("message"), 0, 0), d2, out);
1293
- if (out->end - out->cur <= 2) {
1294
- grow(out, 2);
1295
- }
1296
- *out->cur++ = ',';
1297
- // backtrace
1298
- if (out->end - out->cur <= (long)size) {
1299
- grow(out, size);
1300
- }
1301
- fill_indent(out, d2);
1302
- dump_cstr("~bt", 3, 0, 0, out);
1303
- *out->cur++ = ':';
1304
- dump_val(rb_funcall2(obj, rb_intern("backtrace"), 0, 0), d2, out);
1305
- if (out->end - out->cur <= 2) {
1306
- grow(out, 2);
1307
- }
1308
- if (0 < cnt) {
1309
- *out->cur++ = ',';
1310
- }
1311
- }
1312
- #endif
1313
- for (i = cnt; 0 < i; i--, np++) {
1314
- if (out->end - out->cur <= (long)size) {
1315
- grow(out, size);
1316
- }
1317
- vid = rb_to_id(*np);
1318
- fill_indent(out, d2);
1319
- attr = rb_id2name(vid);
1320
- if ('@' == *attr) {
1321
- attr++;
1322
- dump_cstr(attr, strlen(attr), 0, 0, out);
1323
- } else {
1324
- char buf[32];
1325
-
1326
- *buf = '~';
1327
- strncpy(buf + 1, attr, sizeof(buf) - 2);
1328
- buf[sizeof(buf) - 1] = '\0';
1329
- dump_cstr(buf, strlen(attr) + 1, 0, 0, out);
1330
- }
1331
- *out->cur++ = ':';
1332
- dump_val(rb_ivar_get(obj, vid), d2, out);
1333
- if (out->end - out->cur <= 2) {
1334
- grow(out, 2);
1335
- }
1336
- if (1 < i) {
1337
- *out->cur++ = ',';
1338
- }
1339
- }
1340
- #endif
1341
- out->depth = depth;
1342
- }
1343
- *out->cur++ = '}';
1344
- *out->cur = '\0';
1345
- }
1346
-
1347
- #if HAS_RSTRUCT
1348
- static void
1349
- dump_struct_comp(VALUE obj, int depth, Out out) {
1350
- if (rb_respond_to(obj, oj_to_hash_id)) {
1351
- VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1352
-
1353
- if (T_HASH != rb_type(h)) {
1354
- rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1355
- }
1356
- dump_hash(h, depth, out->opts->mode, out);
1357
- } else if (rb_respond_to(obj, oj_to_json_id)) {
1358
- VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
1359
- const char *s = StringValuePtr(rs);
1360
- int len = (int)RSTRING_LEN(rs);
1361
-
1362
- if (out->end - out->cur <= len) {
1363
- grow(out, len);
1364
- }
1365
- memcpy(out->cur, s, len);
1366
- out->cur += len;
1367
- } else {
1368
- dump_struct_obj(obj, depth, out);
1369
- }
1370
- }
1371
-
1372
- static void
1373
- dump_struct_obj(VALUE obj, int depth, Out out) {
1374
- VALUE clas = rb_obj_class(obj);
1375
- const char *class_name = rb_class2name(clas);
1376
- VALUE *vp;
1377
- int i;
1378
- int d2 = depth + 1;
1379
- int d3 = d2 + 1;
1380
- size_t len = strlen(class_name);
1381
- size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
1382
-
1383
- if (out->end - out->cur <= (long)size) {
1384
- grow(out, size);
1385
- }
1386
- *out->cur++ = '{';
1387
- fill_indent(out, d2);
1388
- *out->cur++ = '"';
1389
- *out->cur++ = '^';
1390
- *out->cur++ = 'u';
1391
- *out->cur++ = '"';
1392
- *out->cur++ = ':';
1393
- *out->cur++ = '[';
1394
- fill_indent(out, d3);
1395
- *out->cur++ = '"';
1396
- memcpy(out->cur, class_name, len);
1397
- out->cur += len;
1398
- *out->cur++ = '"';
1399
- *out->cur++ = ',';
1400
- size = d3 * out->indent + 2;
1401
- for (i = (int)RSTRUCT_LEN(obj), vp = RSTRUCT_PTR(obj); 0 < i; i--, vp++) {
1402
- if (out->end - out->cur <= (long)size) {
1403
- grow(out, size);
1404
- }
1405
- fill_indent(out, d3);
1406
- dump_val(*vp, d3, out);
1407
- *out->cur++ = ',';
1408
- }
1409
- out->cur--;
1410
- *out->cur++ = ']';
1411
- *out->cur++ = '}';
1412
- *out->cur = '\0';
1413
- }
1414
- #endif
1415
-
1416
- static void
1417
- dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
1418
- ID *idp;
1419
- VALUE v;
1420
- const char *name;
1421
- size_t size;
1422
- int d2 = depth + 1;
1423
-
1424
- if (out->end - out->cur <= 2) {
1425
- grow(out, 2);
1426
- }
1427
- *out->cur++ = '{';
1428
- if (0 != clas) {
1429
- const char *class_name = rb_class2name(clas);
1430
- int clen = (int)strlen(class_name);
1431
-
1432
- size = d2 * out->indent + clen + 10;
1433
- if (out->end - out->cur <= (long)size) {
1434
- grow(out, size);
1435
- }
1436
- fill_indent(out, d2);
1437
- *out->cur++ = '"';
1438
- *out->cur++ = '^';
1439
- *out->cur++ = 'O';
1440
- *out->cur++ = '"';
1441
- *out->cur++ = ':';
1442
- dump_cstr(class_name, clen, 0, 0, out);
1443
- *out->cur++ = ',';
1444
- }
1445
- size = d2 * out->indent + 1;
1446
- for (idp = odd->attrs; 0 != *idp; idp++) {
1447
- if (out->end - out->cur <= (long)size) {
1448
- grow(out, size);
1449
- }
1450
- name = rb_id2name(*idp);
1451
- v = rb_funcall(obj, *idp, 0);
1452
- fill_indent(out, d2);
1453
- dump_cstr(name, strlen(name), 0, 0, out);
1454
- *out->cur++ = ':';
1455
- dump_val(v, d2, out);
1456
- if (out->end - out->cur <= 2) {
1457
- grow(out, 2);
1458
- }
1459
- *out->cur++ = ',';
1460
- }
1461
- out->cur--;
1462
- *out->cur++ = '}';
1463
- *out->cur = '\0';
1464
- }
1465
-
1466
- static void
1467
- raise_strict(VALUE obj) {
1468
- rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.\n", rb_class2name(rb_obj_class(obj)));
1469
- }
1470
-
1471
- static void
1472
- dump_val(VALUE obj, int depth, Out out) {
1473
- switch (rb_type(obj)) {
1474
- case T_NIL: dump_nil(out); break;
1475
- case T_TRUE: dump_true(out); break;
1476
- case T_FALSE: dump_false(out); break;
1477
- case T_FIXNUM: dump_fixnum(obj, out); break;
1478
- case T_FLOAT: dump_float(obj, out); break;
1479
- case T_BIGNUM: dump_bignum(obj, out); break;
1480
- case T_STRING:
1481
- switch (out->opts->mode) {
1482
- case StrictMode:
1483
- case NullMode:
1484
- case CompatMode: dump_str_comp(obj, out); break;
1485
- case ObjectMode:
1486
- default: dump_str_obj(obj, out); break;
1487
- }
1488
- break;
1489
- case T_SYMBOL:
1490
- switch (out->opts->mode) {
1491
- case StrictMode: raise_strict(obj); break;
1492
- case NullMode: dump_nil(out); break;
1493
- case CompatMode: dump_sym_comp(obj, out); break;
1494
- case ObjectMode:
1495
- default: dump_sym_obj(obj, out); break;
1496
- }
1497
- break;
1498
- case T_ARRAY: dump_array(obj, depth, out); break;
1499
- case T_HASH: dump_hash(obj, depth, out->opts->mode, out); break;
1500
- case T_CLASS:
1501
- switch (out->opts->mode) {
1502
- case StrictMode: raise_strict(obj); break;
1503
- case NullMode: dump_nil(out); break;
1504
- case CompatMode: dump_class_comp(obj, out); break;
1505
- case ObjectMode:
1506
- default: dump_class_obj(obj, out); break;
1507
- }
1508
- break;
1509
- #if (defined T_RATIONAL && defined RRATIONAL)
1510
- case T_RATIONAL:
1511
- #endif
1512
- case T_OBJECT:
1513
- switch (out->opts->mode) {
1514
- case StrictMode: dump_data_strict(obj, out); break;
1515
- case NullMode: dump_data_null(obj, out); break;
1516
- case CompatMode: dump_obj_comp(obj, depth, out); break;
1517
- case ObjectMode:
1518
- default: dump_obj_obj(obj, depth, out); break;
1519
- }
1520
- break;
1521
- case T_DATA:
1522
- switch (out->opts->mode) {
1523
- case StrictMode: dump_data_strict(obj, out); break;
1524
- case NullMode: dump_data_null(obj, out); break;
1525
- case CompatMode: dump_data_comp(obj, out); break;
1526
- case ObjectMode:
1527
- default: dump_data_obj(obj, depth, out); break;
1528
- }
1529
- break;
1530
- #if HAS_RSTRUCT
1531
- case T_STRUCT: // for Range
1532
- switch (out->opts->mode) {
1533
- case StrictMode: raise_strict(obj); break;
1534
- case NullMode: dump_nil(out); break;
1535
- case CompatMode: dump_struct_comp(obj, depth, out); break;
1536
- case ObjectMode:
1537
- default: dump_struct_obj(obj, depth, out); break;
1538
- }
1539
- break;
1540
- #endif
1541
- #if (defined T_COMPLEX && defined RCOMPLEX)
1542
- case T_COMPLEX:
1543
- #endif
1544
- case T_REGEXP:
1545
- switch (out->opts->mode) {
1546
- case StrictMode: raise_strict(obj); break;
1547
- case NullMode: dump_nil(out); break;
1548
- case CompatMode:
1549
- case ObjectMode:
1550
- default: dump_obj_comp(obj, depth, out); break;
1551
- }
1552
- break;
1553
- default:
1554
- rb_raise(rb_eNotImpError, "Failed to dump '%s' Object (%02x)\n",
1555
- rb_class2name(rb_obj_class(obj)), rb_type(obj));
1556
- break;
1557
- }
1558
- }
1559
-
1560
- static void
1561
- dump_obj_to_json(VALUE obj, Options copts, Out out) {
1562
- out->buf = ALLOC_N(char, 65336);
1563
- out->end = out->buf + 65325; // 1 less than end plus extra for possible errors
1564
- out->cur = out->buf;
1565
- out->circ_cnt = 0;
1566
- out->opts = copts;
1567
- out->hash_cnt = 0;
1568
- if (Yes == copts->circular) {
1569
- oj_cache8_new(&out->circ_cache);
1570
- }
1571
- out->indent = copts->indent;
1572
- dump_val(obj, 0, out);
1573
- if (Yes == copts->circular) {
1574
- oj_cache8_delete(out->circ_cache);
1575
- }
1576
- }
1577
-
1578
- char*
1579
- oj_write_obj_to_str(VALUE obj, Options copts) {
1580
- struct _Out out;
1581
-
1582
- dump_obj_to_json(obj, copts, &out);
1583
-
1584
- return out.buf;
1585
- }
1586
-
1587
- void
1588
- oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
1589
- struct _Out out;
1590
- size_t size;
1591
- FILE *f;
1592
-
1593
- dump_obj_to_json(obj, copts, &out);
1594
- size = out.cur - out.buf;
1595
- if (0 == (f = fopen(path, "w"))) {
1596
- rb_raise(rb_eIOError, "%s\n", strerror(errno));
1597
- }
1598
- if (size != fwrite(out.buf, 1, size, f)) {
1599
- int err = ferror(f);
1600
-
1601
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
1602
- }
1603
- xfree(out.buf);
1604
- fclose(f);
1605
- }
1606
-
1607
- // dump leaf functions
1608
-
1609
- inline static void
1610
- dump_chars(const char *s, size_t size, Out out) {
1611
- if (out->end - out->cur <= (long)size) {
1612
- grow(out, size);
1613
- }
1614
- memcpy(out->cur, s, size);
1615
- out->cur += size;
1616
- *out->cur = '\0';
1617
- }
1618
-
1619
- static void
1620
- dump_leaf_str(Leaf leaf, Out out) {
1621
- switch (leaf->value_type) {
1622
- case STR_VAL:
1623
- dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out);
1624
- break;
1625
- case RUBY_VAL:
1626
- dump_cstr(StringValuePtr(leaf->value), RSTRING_LEN(leaf->value), 0, 0, out);
1627
- break;
1628
- case COL_VAL:
1629
- default:
1630
- rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
1631
- break;
1632
- }
1633
- }
1634
-
1635
- static void
1636
- dump_leaf_fixnum(Leaf leaf, Out out) {
1637
- switch (leaf->value_type) {
1638
- case STR_VAL:
1639
- dump_chars(leaf->str, strlen(leaf->str), out);
1640
- break;
1641
- case RUBY_VAL:
1642
- if (T_BIGNUM == rb_type(leaf->value)) {
1643
- dump_bignum(leaf->value, out);
1644
- } else {
1645
- dump_fixnum(leaf->value, out);
1646
- }
1647
- break;
1648
- case COL_VAL:
1649
- default:
1650
- rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
1651
- break;
1652
- }
1653
- }
1654
-
1655
- static void
1656
- dump_leaf_float(Leaf leaf, Out out) {
1657
- switch (leaf->value_type) {
1658
- case STR_VAL:
1659
- dump_chars(leaf->str, strlen(leaf->str), out);
1660
- break;
1661
- case RUBY_VAL:
1662
- dump_float(leaf->value, out);
1663
- break;
1664
- case COL_VAL:
1665
- default:
1666
- rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
1667
- break;
1668
- }
1669
- }
1670
-
1671
- static void
1672
- dump_leaf_array(Leaf leaf, int depth, Out out) {
1673
- size_t size;
1674
- int d2 = depth + 1;
1675
-
1676
- size = 2;
1677
- if (out->end - out->cur <= (long)size) {
1678
- grow(out, size);
1679
- }
1680
- *out->cur++ = '[';
1681
- if (0 == leaf->elements) {
1682
- *out->cur++ = ']';
1683
- } else {
1684
- Leaf first = leaf->elements->next;
1685
- Leaf e = first;
1686
-
1687
- size = d2 * out->indent + 2;
1688
- do {
1689
- if (out->end - out->cur <= (long)size) {
1690
- grow(out, size);
1691
- }
1692
- fill_indent(out, d2);
1693
- dump_leaf(e, d2, out);
1694
- if (e->next != first) {
1695
- *out->cur++ = ',';
1696
- }
1697
- e = e->next;
1698
- } while (e != first);
1699
- size = depth * out->indent + 1;
1700
- if (out->end - out->cur <= (long)size) {
1701
- grow(out, size);
1702
- }
1703
- fill_indent(out, depth);
1704
- *out->cur++ = ']';
1705
- }
1706
- *out->cur = '\0';
1707
- }
1708
-
1709
- static void
1710
- dump_leaf_hash(Leaf leaf, int depth, Out out) {
1711
- size_t size;
1712
- int d2 = depth + 1;
1713
-
1714
- size = 2;
1715
- if (out->end - out->cur <= (long)size) {
1716
- grow(out, size);
1717
- }
1718
- *out->cur++ = '{';
1719
- if (0 == leaf->elements) {
1720
- *out->cur++ = '}';
1721
- } else {
1722
- Leaf first = leaf->elements->next;
1723
- Leaf e = first;
1724
-
1725
- size = d2 * out->indent + 2;
1726
- do {
1727
- if (out->end - out->cur <= (long)size) {
1728
- grow(out, size);
1729
- }
1730
- fill_indent(out, d2);
1731
- dump_cstr(e->key, strlen(e->key), 0, 0, out);
1732
- *out->cur++ = ':';
1733
- dump_leaf(e, d2, out);
1734
- if (e->next != first) {
1735
- *out->cur++ = ',';
1736
- }
1737
- e = e->next;
1738
- } while (e != first);
1739
- size = depth * out->indent + 1;
1740
- if (out->end - out->cur <= (long)size) {
1741
- grow(out, size);
1742
- }
1743
- fill_indent(out, depth);
1744
- *out->cur++ = '}';
1048
+ assure_size(out, cnt);
1049
+ for (b = buf; '\0' != *b; b++) {
1050
+ *out->cur++ = *b;
1745
1051
  }
1746
1052
  *out->cur = '\0';
1747
1053
  }
1748
-
1749
- static void
1750
- dump_leaf(Leaf leaf, int depth, Out out) {
1751
- switch (leaf->type) {
1752
- case T_NIL:
1753
- dump_nil(out);
1754
- break;
1755
- case T_TRUE:
1756
- dump_true(out);
1757
- break;
1758
- case T_FALSE:
1759
- dump_false(out);
1760
- break;
1761
- case T_STRING:
1762
- dump_leaf_str(leaf, out);
1763
- break;
1764
- case T_FIXNUM:
1765
- dump_leaf_fixnum(leaf, out);
1766
- break;
1767
- case T_FLOAT:
1768
- dump_leaf_float(leaf, out);
1769
- break;
1770
- case T_ARRAY:
1771
- dump_leaf_array(leaf, depth, out);
1772
- break;
1773
- case T_HASH:
1774
- dump_leaf_hash(leaf, depth, out);
1775
- break;
1776
- default:
1777
- rb_raise(rb_eTypeError, "Unexpected type %02x.\n", leaf->type);
1778
- break;
1779
- }
1780
- }
1781
-
1782
- static void
1783
- dump_leaf_to_json(Leaf leaf, Options copts, Out out) {
1784
- out->buf = ALLOC_N(char, 65336);
1785
- out->end = out->buf + 65325; // 10 less than end plus extra for possible errors
1786
- out->cur = out->buf;
1787
- out->circ_cnt = 0;
1788
- out->opts = copts;
1789
- out->hash_cnt = 0;
1790
- out->indent = copts->indent;
1791
- dump_leaf(leaf, 0, out);
1792
- }
1793
-
1794
- char*
1795
- oj_write_leaf_to_str(Leaf leaf, Options copts) {
1796
- struct _Out out;
1797
-
1798
- dump_leaf_to_json(leaf, copts, &out);
1799
-
1800
- return out.buf;
1801
- }
1802
-
1803
- void
1804
- oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts) {
1805
- struct _Out out;
1806
- size_t size;
1807
- FILE *f;
1808
-
1809
- dump_leaf_to_json(leaf, copts, &out);
1810
- size = out.cur - out.buf;
1811
- if (0 == (f = fopen(path, "w"))) {
1812
- rb_raise(rb_eIOError, "%s\n", strerror(errno));
1813
- }
1814
- if (size != fwrite(out.buf, 1, size, f)) {
1815
- int err = ferror(f);
1816
-
1817
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
1818
- }
1819
- xfree(out.buf);
1820
- fclose(f);
1821
- }