oj 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
- }