oj 2.18.5 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -226
  3. data/ext/oj/circarray.c +0 -25
  4. data/ext/oj/circarray.h +0 -25
  5. data/ext/oj/code.c +227 -0
  6. data/ext/oj/code.h +40 -0
  7. data/ext/oj/compat.c +126 -38
  8. data/ext/oj/custom.c +1097 -0
  9. data/ext/oj/dump.c +658 -2376
  10. data/ext/oj/dump.h +92 -0
  11. data/ext/oj/dump_compat.c +937 -0
  12. data/ext/oj/dump_leaf.c +254 -0
  13. data/ext/oj/dump_object.c +810 -0
  14. data/ext/oj/dump_rails.c +329 -0
  15. data/ext/oj/dump_strict.c +416 -0
  16. data/ext/oj/err.c +0 -25
  17. data/ext/oj/err.h +8 -2
  18. data/ext/oj/fast.c +24 -24
  19. data/ext/oj/mimic_json.c +817 -0
  20. data/ext/oj/mimic_rails.c +806 -0
  21. data/ext/oj/mimic_rails.h +17 -0
  22. data/ext/oj/object.c +18 -72
  23. data/ext/oj/odd.c +0 -25
  24. data/ext/oj/odd.h +2 -27
  25. data/ext/oj/oj.c +655 -1503
  26. data/ext/oj/oj.h +93 -40
  27. data/ext/oj/parse.c +99 -46
  28. data/ext/oj/parse.h +12 -26
  29. data/ext/oj/reader.c +1 -25
  30. data/ext/oj/reader.h +3 -25
  31. data/ext/oj/resolve.c +9 -11
  32. data/ext/oj/resolve.h +2 -2
  33. data/ext/oj/rxclass.c +133 -0
  34. data/ext/oj/rxclass.h +27 -0
  35. data/ext/oj/saj.c +4 -25
  36. data/ext/oj/scp.c +3 -25
  37. data/ext/oj/sparse.c +89 -13
  38. data/ext/oj/stream_writer.c +301 -0
  39. data/ext/oj/strict.c +4 -27
  40. data/ext/oj/string_writer.c +480 -0
  41. data/ext/oj/val_stack.h +6 -2
  42. data/lib/oj.rb +1 -23
  43. data/lib/oj/easy_hash.rb +12 -4
  44. data/lib/oj/json.rb +172 -0
  45. data/lib/oj/mimic.rb +123 -18
  46. data/lib/oj/state.rb +131 -0
  47. data/lib/oj/version.rb +1 -1
  48. data/pages/Advanced.md +22 -0
  49. data/pages/Compatibility.md +25 -0
  50. data/pages/Custom.md +23 -0
  51. data/pages/Encoding.md +65 -0
  52. data/pages/JsonGem.md +79 -0
  53. data/pages/Modes.md +140 -0
  54. data/pages/Options.md +250 -0
  55. data/pages/Rails.md +60 -0
  56. data/pages/Security.md +20 -0
  57. data/test/activesupport4/decoding_test.rb +105 -0
  58. data/test/activesupport4/encoding_test.rb +531 -0
  59. data/test/activesupport4/test_helper.rb +41 -0
  60. data/test/activesupport5/decoding_test.rb +125 -0
  61. data/test/activesupport5/encoding_test.rb +483 -0
  62. data/test/activesupport5/encoding_test_cases.rb +90 -0
  63. data/test/activesupport5/test_helper.rb +50 -0
  64. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  65. data/test/json_gem/json_addition_test.rb +216 -0
  66. data/test/json_gem/json_common_interface_test.rb +143 -0
  67. data/test/json_gem/json_encoding_test.rb +109 -0
  68. data/test/json_gem/json_ext_parser_test.rb +20 -0
  69. data/test/json_gem/json_fixtures_test.rb +35 -0
  70. data/test/json_gem/json_generator_test.rb +383 -0
  71. data/test/json_gem/json_generic_object_test.rb +90 -0
  72. data/test/json_gem/json_parser_test.rb +470 -0
  73. data/test/json_gem/json_string_matching_test.rb +42 -0
  74. data/test/json_gem/test_helper.rb +18 -0
  75. data/test/perf_compat.rb +30 -28
  76. data/test/perf_object.rb +1 -1
  77. data/test/perf_strict.rb +18 -1
  78. data/test/sample.rb +0 -1
  79. data/test/test_compat.rb +169 -93
  80. data/test/test_custom.rb +355 -0
  81. data/test/test_file.rb +0 -8
  82. data/test/test_null.rb +376 -0
  83. data/test/test_object.rb +268 -3
  84. data/test/test_scp.rb +22 -1
  85. data/test/test_strict.rb +160 -4
  86. data/test/test_various.rb +52 -620
  87. data/test/tests.rb +14 -0
  88. data/test/tests_mimic.rb +14 -0
  89. data/test/tests_mimic_addition.rb +7 -0
  90. metadata +89 -47
  91. data/test/activesupport_datetime_test.rb +0 -23
  92. data/test/bug.rb +0 -51
  93. data/test/bug2.rb +0 -10
  94. data/test/bug3.rb +0 -46
  95. data/test/bug_fast.rb +0 -32
  96. data/test/bug_load.rb +0 -24
  97. data/test/crash.rb +0 -111
  98. data/test/curl/curl_oj.rb +0 -46
  99. data/test/curl/get_oj.rb +0 -24
  100. data/test/curl/just_curl.rb +0 -31
  101. data/test/curl/just_oj.rb +0 -51
  102. data/test/example.rb +0 -11
  103. data/test/foo.rb +0 -24
  104. data/test/io.rb +0 -48
  105. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  106. data/test/mod.rb +0 -16
  107. data/test/rails.rb +0 -50
  108. data/test/russian.rb +0 -18
  109. data/test/struct.rb +0 -29
  110. data/test/test_serializer.rb +0 -59
  111. data/test/write_timebars.rb +0 -31
@@ -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>
@@ -42,73 +17,24 @@
42
17
 
43
18
  #include "oj.h"
44
19
  #include "cache8.h"
20
+ #include "dump.h"
45
21
  #include "odd.h"
46
22
 
47
- #if !HAS_ENCODING_SUPPORT || defined(RUBINIUS_RUBY)
48
- #define rb_eEncodingError rb_eException
49
- #endif
50
-
51
23
  // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
52
24
  #define OJ_INFINITY (1.0/0.0)
53
25
 
54
- // Extra padding at end of buffer.
55
- #define BUFFER_EXTRA 10
56
-
57
26
  #define MAX_DEPTH 1000
58
27
 
59
- 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;
60
31
 
61
- static void raise_strict(VALUE obj);
62
- static void dump_val(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
63
- static void dump_nil(Out out);
64
- static void dump_true(Out out);
65
- static void dump_false(Out out);
66
- static void dump_fixnum(VALUE obj, Out out);
67
- static void dump_bignum(VALUE obj, Out out);
68
- static void dump_float(VALUE obj, Out out);
69
- static void dump_raw(const char *str, size_t cnt, Out out);
70
- static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
71
- static void dump_hex(uint8_t c, Out out);
72
- static void dump_str_comp(VALUE obj, Out out);
73
- static void dump_str_obj(VALUE obj, VALUE clas, int depth, Out out);
74
- static void dump_sym_comp(VALUE obj, Out out);
75
- static void dump_sym_obj(VALUE obj, Out out);
76
- static void dump_class_comp(VALUE obj, Out out);
77
- static void dump_class_obj(VALUE obj, Out out);
78
- static void dump_array(VALUE obj, VALUE clas, int depth, Out out);
79
- static int hash_cb_strict(VALUE key, VALUE value, Out out);
80
- static int hash_cb_compat(VALUE key, VALUE value, Out out);
81
- static int hash_cb_object(VALUE key, VALUE value, Out out);
82
- static void dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out);
83
- static void dump_time(VALUE obj, Out out, int withZone);
84
- static void dump_ruby_time(VALUE obj, Out out);
85
- static void dump_xml_time(VALUE obj, Out out);
86
- static void dump_data_strict(VALUE obj, Out out);
87
- static void dump_data_null(VALUE obj, Out out);
88
- static void dump_data_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
89
- static void dump_data_obj(VALUE obj, int depth, Out out);
90
- static void dump_obj_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
91
- static void dump_obj_obj(VALUE obj, int depth, Out out);
92
- static void dump_struct_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok);
93
- static void dump_struct_obj(VALUE obj, int depth, Out out);
94
- #if HAS_IVAR_HELPERS
95
- static int dump_attr_cb(ID key, VALUE value, Out out);
96
- #endif
97
- static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
98
- static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out);
32
+ typedef unsigned long ulong;
99
33
 
100
- static void grow(Out out, size_t len);
101
34
  static size_t hibit_friendly_size(const uint8_t *str, size_t len);
102
35
  static size_t xss_friendly_size(const uint8_t *str, size_t len);
103
36
  static size_t ascii_friendly_size(const uint8_t *str, size_t len);
104
37
 
105
- static void dump_leaf(Leaf leaf, int depth, Out out);
106
- static void dump_leaf_str(Leaf leaf, Out out);
107
- static void dump_leaf_fixnum(Leaf leaf, Out out);
108
- static void dump_leaf_float(Leaf leaf, Out out);
109
- static void dump_leaf_array(Leaf leaf, int depth, Out out);
110
- static void dump_leaf_hash(Leaf leaf, int depth, Out out);
111
-
112
38
  static const char hex_chars[17] = "0123456789abcdef";
113
39
 
114
40
  // JSON standard except newlines are no escaped
@@ -156,11 +82,39 @@ static char xss_friendly_chars[256] = "\
156
82
  33333333333333333333333333333333\
157
83
  33333333333333333333333333333333";
158
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
+
159
112
  inline static size_t
160
113
  newline_friendly_size(const uint8_t *str, size_t len) {
161
114
  size_t size = 0;
115
+ size_t i = len;
162
116
 
163
- for (; 0 < len; str++, len--) {
117
+ for (; 0 < i; str++, i--) {
164
118
  size += newline_friendly_chars[*str];
165
119
  }
166
120
  return size - len * (size_t)'0';
@@ -169,8 +123,9 @@ newline_friendly_size(const uint8_t *str, size_t len) {
169
123
  inline static size_t
170
124
  hibit_friendly_size(const uint8_t *str, size_t len) {
171
125
  size_t size = 0;
126
+ size_t i = len;
172
127
 
173
- for (; 0 < len; str++, len--) {
128
+ for (; 0 < i; str++, i--) {
174
129
  size += hibit_friendly_chars[*str];
175
130
  }
176
131
  return size - len * (size_t)'0';
@@ -179,8 +134,9 @@ hibit_friendly_size(const uint8_t *str, size_t len) {
179
134
  inline static size_t
180
135
  ascii_friendly_size(const uint8_t *str, size_t len) {
181
136
  size_t size = 0;
137
+ size_t i = len;
182
138
 
183
- for (; 0 < len; str++, len--) {
139
+ for (; 0 < i; str++, i--) {
184
140
  size += ascii_friendly_chars[*str];
185
141
  }
186
142
  return size - len * (size_t)'0';
@@ -189,67 +145,76 @@ ascii_friendly_size(const uint8_t *str, size_t len) {
189
145
  inline static size_t
190
146
  xss_friendly_size(const uint8_t *str, size_t len) {
191
147
  size_t size = 0;
148
+ size_t i = len;
192
149
 
193
- for (; 0 < len; str++, len--) {
150
+ for (; 0 < i; str++, i--) {
194
151
  size += xss_friendly_chars[*str];
195
152
  }
196
153
  return size - len * (size_t)'0';
197
154
  }
198
155
 
199
- inline static void
200
- fill_indent(Out out, int cnt) {
201
- if (0 < out->indent) {
202
- cnt *= out->indent;
203
- *out->cur++ = '\n';
204
- for (; 0 < cnt; cnt--) {
205
- *out->cur++ = ' ';
206
- }
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;
160
+
161
+ for (; 0 < i; str++, i--) {
162
+ size += hixss_friendly_chars[*str];
207
163
  }
164
+ return size - len * (size_t)'0';
208
165
  }
209
166
 
210
- inline static void
211
- dump_ulong(unsigned long num, Out out) {
212
- char buf[32];
213
- 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;
214
171
 
215
- *b-- = '\0';
216
- if (0 < num) {
217
- for (; 0 < num; num /= 10, b--) {
218
- *b = (num % 10) + '0';
219
- }
220
- b++;
221
- } else {
222
- *b = '0';
172
+ for (; 0 < i; str++, i--) {
173
+ size += rails_friendly_chars[*str];
223
174
  }
224
- for (; '\0' != *b; b++) {
225
- *out->cur++ = *b;
226
- }
227
- *out->cur = '\0';
175
+ return size - len * (size_t)'0';
228
176
  }
229
177
 
230
- static void
231
- grow(Out out, size_t len) {
232
- size_t size = out->end - out->buf;
233
- long pos = out->cur - out->buf;
234
- char *buf;
235
-
236
- size *= 2;
237
- if (size <= len * 2 + pos) {
238
- size += len;
239
- }
240
- if (out->allocated) {
241
- buf = REALLOC_N(out->buf, char, (size + BUFFER_EXTRA));
242
- } else {
243
- buf = ALLOC_N(char, (size + BUFFER_EXTRA));
244
- out->allocated = 1;
245
- memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
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
+ }
246
188
  }
247
- if (0 == buf) {
248
- 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;
249
216
  }
250
- out->buf = buf;
251
- out->end = buf + size;
252
- out->cur = out->buf + pos;
217
+ return str;
253
218
  }
254
219
 
255
220
  inline static void
@@ -261,17 +226,7 @@ dump_hex(uint8_t c, Out out) {
261
226
  *out->cur++ = hex_chars[d];
262
227
  }
263
228
 
264
- static void
265
- dump_raw(const char *str, size_t cnt, Out out) {
266
- if (out->end - out->cur <= (long)cnt + 10) {
267
- grow(out, cnt + 10);
268
- }
269
- memcpy(out->cur, str, cnt);
270
- out->cur += cnt;
271
- *out->cur = '\0';
272
- }
273
-
274
- const char*
229
+ static const char*
275
230
  dump_unicode(const char *str, const char *end, Out out) {
276
231
  uint32_t code = 0;
277
232
  uint8_t b = *(uint8_t*)str;
@@ -294,13 +249,13 @@ dump_unicode(const char *str, const char *end, Out out) {
294
249
  code = b & 0x00000001;
295
250
  } else {
296
251
  cnt = 0;
297
- rb_raise(rb_eEncodingError, "Invalid Unicode\n");
252
+ rb_raise(oj_json_generator_error_class, "Invalid Unicode");
298
253
  }
299
254
  str++;
300
255
  for (; 0 < cnt; cnt--, str++) {
301
256
  b = *(uint8_t*)str;
302
257
  if (end <= str || 0x80 != (0xC0 & b)) {
303
- rb_raise(rb_eEncodingError, "Invalid Unicode\n");
258
+ rb_raise(oj_json_generator_error_class, "Invalid Unicode");
304
259
  }
305
260
  code = (code << 6) | (b & 0x0000003F);
306
261
  }
@@ -324,1912 +279,234 @@ dump_unicode(const char *str, const char *end, Out out) {
324
279
  return str - 1;
325
280
  }
326
281
 
327
- // returns 0 if not using circular references, -1 if not further writing is
328
- // needed (duplicate), and a positive value if the object was added to the cache.
329
- static long
330
- 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) {
331
287
  slot_t id = 0;
332
288
  slot_t *slot;
333
289
 
334
- if (ObjectMode == out->opts->mode && Yes == out->opts->circular) {
290
+ if (Yes == out->opts->circular) {
335
291
  if (0 == (id = oj_cache8_get(out->circ_cache, obj, &slot))) {
336
292
  out->circ_cnt++;
337
293
  id = out->circ_cnt;
338
294
  *slot = id;
339
295
  } else {
340
- if (out->end - out->cur <= 18) {
341
- 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++ = '"';
342
303
  }
343
- *out->cur++ = '"';
344
- *out->cur++ = '^';
345
- *out->cur++ = 'r';
346
- dump_ulong(id, out);
347
- *out->cur++ = '"';
348
-
349
304
  return -1;
350
305
  }
351
306
  }
352
307
  return (long)id;
353
308
  }
354
309
 
355
- static void
356
- dump_nil(Out out) {
357
- size_t size = 4;
358
-
359
- if (out->end - out->cur <= (long)size) {
360
- grow(out, size);
361
- }
362
- *out->cur++ = 'n';
363
- *out->cur++ = 'u';
364
- *out->cur++ = 'l';
365
- *out->cur++ = 'l';
366
- *out->cur = '\0';
367
- }
368
-
369
- static void
370
- dump_true(Out out) {
371
- size_t size = 4;
372
-
373
- if (out->end - out->cur <= (long)size) {
374
- grow(out, size);
375
- }
376
- *out->cur++ = 't';
377
- *out->cur++ = 'r';
378
- *out->cur++ = 'u';
379
- *out->cur++ = 'e';
380
- *out->cur = '\0';
381
- }
382
-
383
- static void
384
- dump_false(Out out) {
385
- size_t size = 5;
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);
386
335
 
387
- if (out->end - out->cur <= (long)size) {
388
- 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';
389
353
  }
390
- *out->cur++ = 'f';
391
- *out->cur++ = 'a';
392
- *out->cur++ = 'l';
393
- *out->cur++ = 's';
394
- *out->cur++ = 'e';
395
- *out->cur = '\0';
396
- }
397
-
398
- static void
399
- dump_fixnum(VALUE obj, Out out) {
400
- char buf[32];
401
- char *b = buf + sizeof(buf) - 1;
402
- long long num = rb_num2ll(obj);
403
- int neg = 0;
404
-
405
- if (0 > num) {
354
+ if (0 > sec) {
406
355
  neg = 1;
407
- num = -num;
356
+ sec = -sec;
357
+ if (0 < nsec) {
358
+ nsec = 1000000000 - nsec;
359
+ sec--;
360
+ }
408
361
  }
409
- *b-- = '\0';
410
- if (0 < num) {
411
- for (; 0 < num; num /= 10, b--) {
412
- *b = (num % 10) + '0';
362
+ dot = b - 9;
363
+ if (0 < out->opts->sec_prec) {
364
+ if (9 > out->opts->sec_prec) {
365
+ int i;
366
+
367
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
368
+ dot++;
369
+ nsec = (nsec + 5) / 10;
370
+ one /= 10;
371
+ }
413
372
  }
414
- if (neg) {
415
- *b = '-';
416
- } else {
417
- b++;
373
+ if (one <= nsec) {
374
+ nsec -= one;
375
+ sec++;
418
376
  }
419
- } else {
420
- *b = '0';
377
+ for (; dot < b; b--, nsec /= 10) {
378
+ *b = '0' + (nsec % 10);
379
+ }
380
+ *b-- = '.';
421
381
  }
422
- if (out->end - out->cur <= (long)(sizeof(buf) - (b - buf))) {
423
- grow(out, sizeof(buf) - (b - buf));
382
+ if (0 == sec) {
383
+ *b-- = '0';
384
+ } else {
385
+ for (; 0 < sec; b--, sec /= 10) {
386
+ *b = '0' + (sec % 10);
387
+ }
424
388
  }
425
- for (; '\0' != *b; b++) {
426
- *out->cur++ = *b;
389
+ if (neg) {
390
+ *b-- = '-';
427
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;
428
397
  *out->cur = '\0';
429
398
  }
430
399
 
431
- static void
432
- dump_bignum(VALUE obj, Out out) {
433
- volatile VALUE rs = rb_big2str(obj, 10);
434
- int cnt = (int)RSTRING_LEN(rs);
400
+ void
401
+ oj_dump_ruby_time(VALUE obj, Out out) {
402
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
435
403
 
436
- if (out->end - out->cur <= (long)cnt) {
437
- grow(out, cnt);
438
- }
439
- memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
440
- out->cur += cnt;
441
- *out->cur = '\0';
404
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
442
405
  }
443
406
 
444
- static const char inf_val[] = INF_VAL;
445
- static const char ninf_val[] = NINF_VAL;
446
- static const char nan_val[] = NAN_VAL;
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 = '+';
447
427
 
448
- // Removed dependencies on math due to problems with CentOS 5.4.
449
- static void
450
- dump_float(VALUE obj, Out out) {
451
- char buf[64];
452
- char *b;
453
- double d = rb_num2dbl(obj);
454
- int cnt = 0;
455
-
456
- if (0.0 == d) {
457
- b = buf;
458
- *b++ = '0';
459
- *b++ = '.';
460
- *b++ = '0';
461
- *b++ = '\0';
462
- cnt = 3;
463
- } else if (OJ_INFINITY == d) {
464
- if (ObjectMode == out->opts->mode) {
465
- strcpy(buf, inf_val);
466
- cnt = sizeof(inf_val) - 1;
467
- } else {
468
- NanDump nd = out->opts->dump_opts.nan_dump;
469
-
470
- if (AutoNan == nd) {
471
- switch (out->opts->mode) {
472
- case CompatMode: nd = WordNan; break;
473
- case StrictMode: nd = RaiseNan; break;
474
- case NullMode: nd = NullNan; break;
475
- default: break;
476
- }
477
- }
478
- switch (nd) {
479
- case RaiseNan:
480
- raise_strict(obj);
481
- break;
482
- case WordNan:
483
- strcpy(buf, "Infinity");
484
- cnt = 8;
485
- break;
486
- case NullNan:
487
- strcpy(buf, "null");
488
- cnt = 4;
489
- break;
490
- case HugeNan:
491
- default:
492
- strcpy(buf, inf_val);
493
- cnt = sizeof(inf_val) - 1;
494
- break;
495
- }
496
- }
497
- } else if (-OJ_INFINITY == d) {
498
- if (ObjectMode == out->opts->mode) {
499
- strcpy(buf, ninf_val);
500
- cnt = sizeof(ninf_val) - 1;
501
- } else {
502
- NanDump nd = out->opts->dump_opts.nan_dump;
503
-
504
- if (AutoNan == nd) {
505
- switch (out->opts->mode) {
506
- case CompatMode: nd = WordNan; break;
507
- case StrictMode: nd = RaiseNan; break;
508
- case NullMode: nd = NullNan; break;
509
- default: break;
510
- }
511
- }
512
- switch (nd) {
513
- case RaiseNan:
514
- raise_strict(obj);
515
- break;
516
- case WordNan:
517
- strcpy(buf, "-Infinity");
518
- cnt = 9;
519
- break;
520
- case NullNan:
521
- strcpy(buf, "null");
522
- cnt = 4;
523
- break;
524
- case HugeNan:
525
- default:
526
- strcpy(buf, ninf_val);
527
- cnt = sizeof(ninf_val) - 1;
528
- break;
529
- }
530
- }
531
- } else if (isnan(d)) {
532
- if (ObjectMode == out->opts->mode) {
533
- strcpy(buf, nan_val);
534
- cnt = sizeof(nan_val) - 1;
535
- } else {
536
- NanDump nd = out->opts->dump_opts.nan_dump;
537
-
538
- if (AutoNan == nd) {
539
- switch (out->opts->mode) {
540
- case CompatMode: nd = WordNan; break;
541
- case StrictMode: nd = RaiseNan; break;
542
- case NullMode: nd = NullNan; break;
543
- default: break;
544
- }
545
- }
546
- switch (nd) {
547
- case RaiseNan:
548
- raise_strict(obj);
549
- break;
550
- case WordNan:
551
- strcpy(buf, "NaN");
552
- cnt = 3;
553
- break;
554
- case NullNan:
555
- strcpy(buf, "null");
556
- cnt = 4;
557
- break;
558
- case HugeNan:
559
- default:
560
- strcpy(buf, nan_val);
561
- cnt = sizeof(nan_val) - 1;
562
- break;
563
- }
564
- }
565
- } else if (d == (double)(long long int)d) {
566
- cnt = snprintf(buf, sizeof(buf), "%.1f", d);
567
- } else if (0 == out->opts->float_prec) {
568
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
569
-
570
- cnt = (int)RSTRING_LEN(rstr);
571
- if ((int)sizeof(buf) <= cnt) {
572
- cnt = sizeof(buf) - 1;
573
- }
574
- strncpy(buf, rb_string_value_ptr((VALUE*)&rstr), cnt);
575
- buf[cnt] = '\0';
576
- } else {
577
- cnt = snprintf(buf, sizeof(buf), out->opts->float_fmt, d);
578
- }
579
- if (out->end - out->cur <= (long)cnt) {
580
- grow(out, cnt);
581
- }
582
- for (b = buf; '\0' != *b; b++) {
583
- *out->cur++ = *b;
584
- }
585
- *out->cur = '\0';
586
- }
587
-
588
- static void
589
- dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out) {
590
- size_t size;
591
- char *cmap;
592
-
593
- switch (out->opts->escape_mode) {
594
- case NLEsc:
595
- cmap = newline_friendly_chars;
596
- size = newline_friendly_size((uint8_t*)str, cnt);
597
- break;
598
- case ASCIIEsc:
599
- cmap = ascii_friendly_chars;
600
- size = ascii_friendly_size((uint8_t*)str, cnt);
601
- break;
602
- case XSSEsc:
603
- cmap = xss_friendly_chars;
604
- size = xss_friendly_size((uint8_t*)str, cnt);
605
- break;
606
- case JSONEsc:
607
- default:
608
- cmap = hibit_friendly_chars;
609
- size = hibit_friendly_size((uint8_t*)str, cnt);
610
- }
611
- if (out->end - out->cur <= (long)size + BUFFER_EXTRA) { // extra 10 for escaped first char, quotes, and sym
612
- grow(out, size + BUFFER_EXTRA);
613
- }
614
- *out->cur++ = '"';
615
- if (escape1) {
616
- *out->cur++ = '\\';
617
- *out->cur++ = 'u';
618
- *out->cur++ = '0';
619
- *out->cur++ = '0';
620
- dump_hex((uint8_t)*str, out);
621
- cnt--;
622
- size--;
623
- str++;
624
- is_sym = 0; // just to make sure
625
- }
626
- if (cnt == size) {
627
- if (is_sym) {
628
- *out->cur++ = ':';
629
- }
630
- for (; '\0' != *str; str++) {
631
- *out->cur++ = *str;
632
- }
633
- *out->cur++ = '"';
634
- } else {
635
- const char *end = str + cnt;
636
-
637
- if (is_sym) {
638
- *out->cur++ = ':';
639
- }
640
- for (; str < end; str++) {
641
- switch (cmap[(uint8_t)*str]) {
642
- case '1':
643
- *out->cur++ = *str;
644
- break;
645
- case '2':
646
- *out->cur++ = '\\';
647
- switch (*str) {
648
- case '\\': *out->cur++ = '\\'; break;
649
- case '\b': *out->cur++ = 'b'; break;
650
- case '\t': *out->cur++ = 't'; break;
651
- case '\n': *out->cur++ = 'n'; break;
652
- case '\f': *out->cur++ = 'f'; break;
653
- case '\r': *out->cur++ = 'r'; break;
654
- default: *out->cur++ = *str; break;
655
- }
656
- break;
657
- case '3': // Unicode
658
- str = dump_unicode(str, end, out);
659
- break;
660
- case '6': // control characters
661
- *out->cur++ = '\\';
662
- *out->cur++ = 'u';
663
- *out->cur++ = '0';
664
- *out->cur++ = '0';
665
- dump_hex((uint8_t)*str, out);
666
- break;
667
- default:
668
- break; // ignore, should never happen if the table is correct
669
- }
670
- }
671
- *out->cur++ = '"';
672
- }
673
- *out->cur = '\0';
674
- }
675
-
676
- static void
677
- dump_str_comp(VALUE obj, Out out) {
678
- dump_cstr(rb_string_value_ptr((VALUE*)&obj), (int)RSTRING_LEN(obj), 0, 0, out);
679
- }
680
-
681
- static void
682
- dump_str_obj(VALUE obj, VALUE clas, int depth, Out out) {
683
- if (Qundef != clas && rb_cString != clas) {
684
- dump_obj_attrs(obj, clas, 0, depth, out);
685
- } else {
686
- const char *s = rb_string_value_ptr((VALUE*)&obj);
687
- size_t len = (size_t)RSTRING_LEN(obj);
688
- char s1 = s[1];
689
-
690
- dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
691
- }
692
- }
693
-
694
- static void
695
- dump_sym_comp(VALUE obj, Out out) {
696
- const char *sym = rb_id2name(SYM2ID(obj));
697
-
698
- dump_cstr(sym, strlen(sym), 0, 0, out);
699
- }
700
-
701
- static void
702
- dump_sym_obj(VALUE obj, Out out) {
703
- const char *sym = rb_id2name(SYM2ID(obj));
704
- size_t len = strlen(sym);
705
-
706
- dump_cstr(sym, len, 1, 0, out);
707
- }
708
-
709
- static void
710
- dump_class_comp(VALUE obj, Out out) {
711
- const char *s = rb_class2name(obj);
712
-
713
- dump_cstr(s, strlen(s), 0, 0, out);
714
- }
715
-
716
- static void
717
- dump_class_obj(VALUE obj, Out out) {
718
- const char *s = rb_class2name(obj);
719
- size_t len = strlen(s);
720
-
721
- if (out->end - out->cur <= 6) {
722
- grow(out, 6);
723
- }
724
- *out->cur++ = '{';
725
- *out->cur++ = '"';
726
- *out->cur++ = '^';
727
- *out->cur++ = 'c';
728
- *out->cur++ = '"';
729
- *out->cur++ = ':';
730
- dump_cstr(s, len, 0, 0, out);
731
- *out->cur++ = '}';
732
- *out->cur = '\0';
733
- }
734
-
735
- static void
736
- dump_array(VALUE a, VALUE clas, int depth, Out out) {
737
- size_t size;
738
- int i, cnt;
739
- int d2 = depth + 1;
740
- long id = check_circular(a, out);
741
-
742
- if (id < 0) {
743
- return;
744
- }
745
- if (Qundef != clas && rb_cArray != clas && ObjectMode == out->opts->mode) {
746
- dump_obj_attrs(a, clas, 0, depth, out);
747
- return;
748
- }
749
- cnt = (int)RARRAY_LEN(a);
750
- *out->cur++ = '[';
751
- if (0 < id) {
752
- size = d2 * out->indent + 16;
753
- if (out->end - out->cur <= (long)size) {
754
- grow(out, size);
755
- }
756
- fill_indent(out, d2);
757
- *out->cur++ = '"';
758
- *out->cur++ = '^';
759
- *out->cur++ = 'i';
760
- dump_ulong(id, out);
761
- *out->cur++ = '"';
762
- }
763
- size = 2;
764
- if (out->end - out->cur <= (long)size) {
765
- grow(out, size);
766
- }
767
- if (0 == cnt) {
768
- *out->cur++ = ']';
769
- } else {
770
- if (0 < id) {
771
- *out->cur++ = ',';
772
- }
773
- if (out->opts->dump_opts.use) {
774
- size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
775
- } else {
776
- size = d2 * out->indent + 2;
777
- }
778
- cnt--;
779
- for (i = 0; i <= cnt; i++) {
780
- if (out->end - out->cur <= (long)size) {
781
- grow(out, size);
782
- }
783
- if (out->opts->dump_opts.use) {
784
- if (0 < out->opts->dump_opts.array_size) {
785
- strcpy(out->cur, out->opts->dump_opts.array_nl);
786
- out->cur += out->opts->dump_opts.array_size;
787
- }
788
- if (0 < out->opts->dump_opts.indent_size) {
789
- int i;
790
- for (i = d2; 0 < i; i--) {
791
- strcpy(out->cur, out->opts->dump_opts.indent_str);
792
- out->cur += out->opts->dump_opts.indent_size;
793
- }
794
- }
795
- } else {
796
- fill_indent(out, d2);
797
- }
798
- dump_val(rb_ary_entry(a, i), d2, out, 0, 0, true);
799
- if (i < cnt) {
800
- *out->cur++ = ',';
801
- }
802
- }
803
- size = depth * out->indent + 1;
804
- if (out->end - out->cur <= (long)size) {
805
- grow(out, size);
806
- }
807
- if (out->opts->dump_opts.use) {
808
- //printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
809
- if (0 < out->opts->dump_opts.array_size) {
810
- strcpy(out->cur, out->opts->dump_opts.array_nl);
811
- out->cur += out->opts->dump_opts.array_size;
812
- }
813
- if (0 < out->opts->dump_opts.indent_size) {
814
- int i;
815
-
816
- for (i = depth; 0 < i; i--) {
817
- strcpy(out->cur, out->opts->dump_opts.indent_str);
818
- out->cur += out->opts->dump_opts.indent_size;
819
- }
820
- }
821
- } else {
822
- fill_indent(out, depth);
823
- }
824
- *out->cur++ = ']';
825
- }
826
- *out->cur = '\0';
827
- }
828
-
829
- static int
830
- hash_cb_strict(VALUE key, VALUE value, Out out) {
831
- int depth = out->depth;
832
- long size;
833
- int rtype = rb_type(key);
834
-
835
- if (rtype != T_STRING && rtype != T_SYMBOL) {
836
- rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));
837
- }
838
- if (out->omit_nil && Qnil == value) {
839
- return ST_CONTINUE;
840
- }
841
- if (!out->opts->dump_opts.use) {
842
- size = depth * out->indent + 1;
843
- if (out->end - out->cur <= size) {
844
- grow(out, size);
845
- }
846
- fill_indent(out, depth);
847
- if (rtype == T_STRING) {
848
- dump_str_comp(key, out);
849
- } else {
850
- dump_sym_comp(key, out);
851
- }
852
- *out->cur++ = ':';
853
- } else {
854
- size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
855
- if (out->end - out->cur <= size) {
856
- grow(out, size);
857
- }
858
- if (0 < out->opts->dump_opts.hash_size) {
859
- strcpy(out->cur, out->opts->dump_opts.hash_nl);
860
- out->cur += out->opts->dump_opts.hash_size;
861
- }
862
- if (0 < out->opts->dump_opts.indent_size) {
863
- int i;
864
- for (i = depth; 0 < i; i--) {
865
- strcpy(out->cur, out->opts->dump_opts.indent_str);
866
- out->cur += out->opts->dump_opts.indent_size;
867
- }
868
- }
869
- if (rtype == T_STRING) {
870
- dump_str_comp(key, out);
871
- } else {
872
- dump_sym_comp(key, out);
873
- }
874
- size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
875
- if (out->end - out->cur <= size) {
876
- grow(out, size);
877
- }
878
- if (0 < out->opts->dump_opts.before_size) {
879
- strcpy(out->cur, out->opts->dump_opts.before_sep);
880
- out->cur += out->opts->dump_opts.before_size;
881
- }
882
- *out->cur++ = ':';
883
- if (0 < out->opts->dump_opts.after_size) {
884
- strcpy(out->cur, out->opts->dump_opts.after_sep);
885
- out->cur += out->opts->dump_opts.after_size;
886
- }
887
- }
888
- dump_val(value, depth, out, 0, 0, false);
889
- out->depth = depth;
890
- *out->cur++ = ',';
891
-
892
- return ST_CONTINUE;
893
- }
894
-
895
- static int
896
- hash_cb_compat(VALUE key, VALUE value, Out out) {
897
- int depth = out->depth;
898
- long size;
899
-
900
- if (out->omit_nil && Qnil == value) {
901
- return ST_CONTINUE;
902
- }
903
- if (!out->opts->dump_opts.use) {
904
- size = depth * out->indent + 1;
905
- if (out->end - out->cur <= size) {
906
- grow(out, size);
907
- }
908
- fill_indent(out, depth);
909
- } else {
910
- size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
911
- if (out->end - out->cur <= size) {
912
- grow(out, size);
913
- }
914
- if (0 < out->opts->dump_opts.hash_size) {
915
- strcpy(out->cur, out->opts->dump_opts.hash_nl);
916
- out->cur += out->opts->dump_opts.hash_size;
917
- }
918
- if (0 < out->opts->dump_opts.indent_size) {
919
- int i;
920
- for (i = depth; 0 < i; i--) {
921
- strcpy(out->cur, out->opts->dump_opts.indent_str);
922
- out->cur += out->opts->dump_opts.indent_size;
923
- }
924
- }
925
- }
926
- switch (rb_type(key)) {
927
- case T_STRING:
928
- dump_str_comp(key, out);
929
- break;
930
- case T_SYMBOL:
931
- dump_sym_comp(key, out);
932
- break;
933
- default:
934
- /*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
935
- dump_str_comp(rb_funcall(key, oj_to_s_id, 0), out);
936
- break;
937
- }
938
- if (!out->opts->dump_opts.use) {
939
- *out->cur++ = ':';
940
- } else {
941
- size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
942
- if (out->end - out->cur <= size) {
943
- grow(out, size);
944
- }
945
- if (0 < out->opts->dump_opts.before_size) {
946
- strcpy(out->cur, out->opts->dump_opts.before_sep);
947
- out->cur += out->opts->dump_opts.before_size;
948
- }
949
- *out->cur++ = ':';
950
- if (0 < out->opts->dump_opts.after_size) {
951
- strcpy(out->cur, out->opts->dump_opts.after_sep);
952
- out->cur += out->opts->dump_opts.after_size;
953
- }
954
- }
955
- dump_val(value, depth, out, 0, 0, true);
956
- out->depth = depth;
957
- *out->cur++ = ',';
958
-
959
- return ST_CONTINUE;
960
- }
961
-
962
- static int
963
- hash_cb_object(VALUE key, VALUE value, Out out) {
964
- int depth = out->depth;
965
- long size = depth * out->indent + 1;
966
-
967
- if (out->omit_nil && Qnil == value) {
968
- return ST_CONTINUE;
969
- }
970
- if (out->end - out->cur <= size) {
971
- grow(out, size);
972
- }
973
- fill_indent(out, depth);
974
- if (rb_type(key) == T_STRING) {
975
- dump_str_obj(key, Qundef, depth, out);
976
- *out->cur++ = ':';
977
- dump_val(value, depth, out, 0, 0, true);
978
- } else if (rb_type(key) == T_SYMBOL) {
979
- dump_sym_obj(key, out);
980
- *out->cur++ = ':';
981
- dump_val(value, depth, out, 0, 0, true);
982
- } else {
983
- int d2 = depth + 1;
984
- long s2 = size + out->indent + 1;
985
- int i;
986
- int started = 0;
987
- uint8_t b;
988
-
989
- if (out->end - out->cur <= s2 + 15) {
990
- grow(out, s2 + 15);
991
- }
992
- *out->cur++ = '"';
993
- *out->cur++ = '^';
994
- *out->cur++ = '#';
995
- out->hash_cnt++;
996
- for (i = 28; 0 <= i; i -= 4) {
997
- b = (uint8_t)((out->hash_cnt >> i) & 0x0000000F);
998
- if ('\0' != b) {
999
- started = 1;
1000
- }
1001
- if (started) {
1002
- *out->cur++ = hex_chars[b];
1003
- }
1004
- }
1005
- *out->cur++ = '"';
1006
- *out->cur++ = ':';
1007
- *out->cur++ = '[';
1008
- fill_indent(out, d2);
1009
- dump_val(key, d2, out, 0, 0, true);
1010
- if (out->end - out->cur <= (long)s2) {
1011
- grow(out, s2);
1012
- }
1013
- *out->cur++ = ',';
1014
- fill_indent(out, d2);
1015
- dump_val(value, d2, out, 0, 0, true);
1016
- if (out->end - out->cur <= (long)size) {
1017
- grow(out, size);
1018
- }
1019
- fill_indent(out, depth);
1020
- *out->cur++ = ']';
1021
- }
1022
- out->depth = depth;
1023
- *out->cur++ = ',';
1024
-
1025
- return ST_CONTINUE;
1026
- }
1027
-
1028
- static void
1029
- dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out) {
1030
- int cnt;
1031
- size_t size;
1032
-
1033
- if (Qundef != clas && rb_cHash != clas && ObjectMode == mode) {
1034
- dump_obj_attrs(obj, clas, 0, depth, out);
1035
- return;
1036
- }
1037
- cnt = (int)RHASH_SIZE(obj);
1038
- size = depth * out->indent + 2;
1039
- if (out->end - out->cur <= 2) {
1040
- grow(out, 2);
1041
- }
1042
- if (0 == cnt) {
1043
- *out->cur++ = '{';
1044
- *out->cur++ = '}';
1045
- } else {
1046
- long id = check_circular(obj, out);
1047
-
1048
- if (0 > id) {
1049
- return;
1050
- }
1051
- *out->cur++ = '{';
1052
- if (0 < id) {
1053
- if (out->end - out->cur <= (long)size + 16) {
1054
- grow(out, size + 16);
1055
- }
1056
- fill_indent(out, depth + 1);
1057
- *out->cur++ = '"';
1058
- *out->cur++ = '^';
1059
- *out->cur++ = 'i';
1060
- *out->cur++ = '"';
1061
- *out->cur++ = ':';
1062
- dump_ulong(id, out);
1063
- *out->cur++ = ',';
1064
- }
1065
- out->depth = depth + 1;
1066
- if (ObjectMode == mode) {
1067
- rb_hash_foreach(obj, hash_cb_object, (VALUE)out);
1068
- } else if (CompatMode == mode) {
1069
- rb_hash_foreach(obj, hash_cb_compat, (VALUE)out);
1070
- } else {
1071
- rb_hash_foreach(obj, hash_cb_strict, (VALUE)out);
1072
- }
1073
- if (',' == *(out->cur - 1)) {
1074
- out->cur--; // backup to overwrite last comma
1075
- }
1076
- if (!out->opts->dump_opts.use) {
1077
- if (out->end - out->cur <= (long)size) {
1078
- grow(out, size);
1079
- }
1080
- fill_indent(out, depth);
1081
- } else {
1082
- size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
1083
- if (out->end - out->cur <= (long)size) {
1084
- grow(out, size);
1085
- }
1086
- if (0 < out->opts->dump_opts.hash_size) {
1087
- strcpy(out->cur, out->opts->dump_opts.hash_nl);
1088
- out->cur += out->opts->dump_opts.hash_size;
1089
- }
1090
- if (0 < out->opts->dump_opts.indent_size) {
1091
- int i;
1092
-
1093
- for (i = depth; 0 < i; i--) {
1094
- strcpy(out->cur, out->opts->dump_opts.indent_str);
1095
- out->cur += out->opts->dump_opts.indent_size;
1096
- }
1097
- }
1098
- }
1099
- *out->cur++ = '}';
1100
- }
1101
- *out->cur = '\0';
1102
- }
1103
-
1104
- static void
1105
- dump_time(VALUE obj, Out out, int withZone) {
1106
- char buf[64];
1107
- char *b = buf + sizeof(buf) - 1;
1108
- long size;
1109
- char *dot;
1110
- int neg = 0;
1111
- long one = 1000000000;
1112
- #if HAS_RB_TIME_TIMESPEC
1113
- struct timespec ts = rb_time_timespec(obj);
1114
- time_t sec = ts.tv_sec;
1115
- long nsec = ts.tv_nsec;
1116
- #else
1117
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
1118
- #if HAS_NANO_TIME
1119
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
1120
- #else
1121
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
1122
- #endif
1123
- #endif
1124
-
1125
- *b-- = '\0';
1126
- if (withZone) {
1127
- long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
1128
- int zneg = (0 > tzsecs);
1129
-
1130
- if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
1131
- tzsecs = 86400;
1132
- }
1133
- if (zneg) {
1134
- tzsecs = -tzsecs;
1135
- }
1136
- if (0 == tzsecs) {
1137
- *b-- = '0';
1138
- } else {
1139
- for (; 0 < tzsecs; b--, tzsecs /= 10) {
1140
- *b = '0' + (tzsecs % 10);
1141
- }
1142
- if (zneg) {
1143
- *b-- = '-';
1144
- }
1145
- }
1146
- *b-- = 'e';
1147
- }
1148
- if (0 > sec) {
1149
- neg = 1;
1150
- sec = -sec;
1151
- if (0 < nsec) {
1152
- nsec = 1000000000 - nsec;
1153
- sec--;
1154
- }
1155
- }
1156
- dot = b - 9;
1157
- if (0 < out->opts->sec_prec) {
1158
- if (9 > out->opts->sec_prec) {
1159
- int i;
1160
-
1161
- for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
1162
- dot++;
1163
- nsec = (nsec + 5) / 10;
1164
- one /= 10;
1165
- }
1166
- }
1167
- if (one <= nsec) {
1168
- nsec -= one;
1169
- sec++;
1170
- }
1171
- for (; dot < b; b--, nsec /= 10) {
1172
- *b = '0' + (nsec % 10);
1173
- }
1174
- *b-- = '.';
1175
- }
1176
- if (0 == sec) {
1177
- *b-- = '0';
1178
- } else {
1179
- for (; 0 < sec; b--, sec /= 10) {
1180
- *b = '0' + (sec % 10);
1181
- }
1182
- }
1183
- if (neg) {
1184
- *b-- = '-';
1185
- }
1186
- b++;
1187
- size = sizeof(buf) - (b - buf) - 1;
1188
- if (out->end - out->cur <= size) {
1189
- grow(out, size);
1190
- }
1191
- memcpy(out->cur, b, size);
1192
- out->cur += size;
1193
- *out->cur = '\0';
1194
- }
1195
-
1196
- static void
1197
- dump_ruby_time(VALUE obj, Out out) {
1198
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1199
-
1200
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1201
- }
1202
-
1203
- static void
1204
- dump_xml_time(VALUE obj, Out out) {
1205
- char buf[64];
1206
- struct tm *tm;
1207
- long one = 1000000000;
1208
- #if HAS_RB_TIME_TIMESPEC
1209
- struct timespec ts = rb_time_timespec(obj);
1210
- time_t sec = ts.tv_sec;
1211
- long nsec = ts.tv_nsec;
1212
- #else
1213
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
1214
- #if HAS_NANO_TIME
1215
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
1216
- #else
1217
- long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
1218
- #endif
1219
- #endif
1220
- long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
1221
- int tzhour, tzmin;
1222
- char tzsign = '+';
1223
-
1224
- if (out->end - out->cur <= 36) {
1225
- grow(out, 36);
1226
- }
1227
- if (9 > out->opts->sec_prec) {
1228
- int i;
1229
-
1230
- for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
1231
- nsec = (nsec + 5) / 10;
1232
- one /= 10;
1233
- }
1234
- if (one <= nsec) {
1235
- nsec -= one;
1236
- sec++;
1237
- }
1238
- }
1239
- // 2012-01-05T23:58:07.123456000+09:00
1240
- //tm = localtime(&sec);
1241
- sec += tzsecs;
1242
- tm = gmtime(&sec);
1243
- #if 1
1244
- if (0 > tzsecs) {
1245
- tzsign = '-';
1246
- tzhour = (int)(tzsecs / -3600);
1247
- tzmin = (int)(tzsecs / -60) - (tzhour * 60);
1248
- } else {
1249
- tzhour = (int)(tzsecs / 3600);
1250
- tzmin = (int)(tzsecs / 60) - (tzhour * 60);
1251
- }
1252
- #else
1253
- if (0 > tm->tm_gmtoff) {
1254
- tzsign = '-';
1255
- tzhour = (int)(tm->tm_gmtoff / -3600);
1256
- tzmin = (int)(tm->tm_gmtoff / -60) - (tzhour * 60);
1257
- } else {
1258
- tzhour = (int)(tm->tm_gmtoff / 3600);
1259
- tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
1260
- }
1261
- #endif
1262
- if (0 == nsec || 0 == out->opts->sec_prec) {
1263
- if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
1264
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
1265
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1266
- tm->tm_hour, tm->tm_min, tm->tm_sec);
1267
- dump_cstr(buf, 20, 0, 0, out);
1268
- } else {
1269
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
1270
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1271
- tm->tm_hour, tm->tm_min, tm->tm_sec,
1272
- tzsign, tzhour, tzmin);
1273
- dump_cstr(buf, 25, 0, 0, out);
1274
- }
1275
- } else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
1276
- char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
1277
- int len = 30;
1278
-
1279
- if (9 > out->opts->sec_prec) {
1280
- format[32] = '0' + out->opts->sec_prec;
1281
- len -= 9 - out->opts->sec_prec;
1282
- }
1283
- sprintf(buf, format,
1284
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1285
- tm->tm_hour, tm->tm_min, tm->tm_sec, nsec);
1286
- dump_cstr(buf, len, 0, 0, out);
1287
- } else {
1288
- char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
1289
- int len = 35;
1290
-
1291
- if (9 > out->opts->sec_prec) {
1292
- format[32] = '0' + out->opts->sec_prec;
1293
- len -= 9 - out->opts->sec_prec;
1294
- }
1295
- sprintf(buf, format,
1296
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1297
- tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
1298
- tzsign, tzhour, tzmin);
1299
- dump_cstr(buf, len, 0, 0, out);
1300
- }
1301
- }
1302
-
1303
- static void
1304
- dump_data_strict(VALUE obj, Out out) {
1305
- VALUE clas = rb_obj_class(obj);
1306
-
1307
- if (oj_bigdecimal_class == clas) {
1308
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1309
-
1310
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
1311
- } else {
1312
- raise_strict(obj);
1313
- }
1314
- }
1315
-
1316
- static void
1317
- dump_data_null(VALUE obj, Out out) {
1318
- VALUE clas = rb_obj_class(obj);
1319
-
1320
- if (oj_bigdecimal_class == clas) {
1321
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1322
-
1323
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
1324
- } else {
1325
- dump_nil(out);
1326
- }
1327
- }
1328
-
1329
- static void
1330
- dump_data_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
1331
- VALUE clas = rb_obj_class(obj);
1332
-
1333
- if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
1334
- volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1335
-
1336
- if (T_HASH != rb_type(h)) {
1337
- // It seems that ActiveRecord implemented to_hash so that it returns
1338
- // an Array and not a Hash. To get around that any value returned
1339
- // will be dumped.
1340
-
1341
- //rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1342
- dump_val(h, depth, out, 0, 0, false);
1343
- }
1344
- dump_hash(h, Qundef, depth, out->opts->mode, out);
1345
- } else if (Yes == out->opts->bigdec_as_num && oj_bigdecimal_class == clas) {
1346
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1347
-
1348
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
1349
- } else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
1350
- volatile VALUE aj;
1351
-
1352
- #if HAS_METHOD_ARITY
1353
- // Some classes elect to not take an options argument so check the arity
1354
- // of as_json.
1355
- switch (rb_obj_method_arity(obj, oj_as_json_id)) {
1356
- case 0:
1357
- aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
1358
- break;
1359
- case 1:
1360
- if (1 <= argc) {
1361
- aj = rb_funcall2(obj, oj_as_json_id, 1, argv);
1362
- } else {
1363
- VALUE nothing [1];
1364
-
1365
- nothing[0] = Qnil;
1366
- aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
1367
- }
1368
- break;
1369
- default:
1370
- aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
1371
- break;
1372
- }
1373
- #else
1374
- aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
1375
- #endif
1376
- // Catch the obvious brain damaged recursive dumping.
1377
- if (aj == obj) {
1378
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1379
-
1380
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1381
- } else {
1382
- dump_val(aj, depth, out, 0, 0, false);
1383
- }
1384
- } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
1385
- volatile VALUE rs;
1386
- const char *s;
1387
- int len;
1388
-
1389
- rs = rb_funcall(obj, oj_to_json_id, 0);
1390
- s = rb_string_value_ptr((VALUE*)&rs);
1391
- len = (int)RSTRING_LEN(rs);
1392
-
1393
- if (out->end - out->cur <= len + 1) {
1394
- grow(out, len);
1395
- }
1396
- memcpy(out->cur, s, len);
1397
- out->cur += len;
1398
- *out->cur = '\0';
1399
- } else {
1400
- if (rb_cTime == clas) {
1401
- switch (out->opts->time_format) {
1402
- case RubyTime: dump_ruby_time(obj, out); break;
1403
- case XmlTime: dump_xml_time(obj, out); break;
1404
- case UnixZTime: dump_time(obj, out, 1); break;
1405
- case UnixTime:
1406
- default: dump_time(obj, out, 0); break;
1407
- }
1408
- } else if (oj_bigdecimal_class == clas) {
1409
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1410
-
1411
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1412
- } else {
1413
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1414
-
1415
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1416
- }
1417
- }
1418
- }
1419
-
1420
- static void
1421
- dump_data_obj(VALUE obj, int depth, Out out) {
1422
- VALUE clas = rb_obj_class(obj);
1423
-
1424
- if (rb_cTime == clas) {
1425
- if (out->end - out->cur <= 6) {
1426
- grow(out, 6);
1427
- }
1428
- *out->cur++ = '{';
1429
- *out->cur++ = '"';
1430
- *out->cur++ = '^';
1431
- *out->cur++ = 't';
1432
- *out->cur++ = '"';
1433
- *out->cur++ = ':';
1434
- switch (out->opts->time_format) {
1435
- case RubyTime: // Does not output fractional seconds
1436
- case XmlTime:
1437
- dump_xml_time(obj, out);
1438
- break;
1439
- case UnixZTime:
1440
- dump_time(obj, out, 1);
1441
- break;
1442
- case UnixTime:
1443
- default:
1444
- dump_time(obj, out, 0);
1445
- break;
1446
- }
1447
- *out->cur++ = '}';
1448
- *out->cur = '\0';
1449
- } else {
1450
- if (oj_bigdecimal_class == clas) {
1451
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1452
-
1453
- if (Yes == out->opts->bigdec_as_num) {
1454
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
1455
- } else {
1456
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1457
- }
1458
- } else {
1459
- dump_nil(out);
1460
- }
1461
- }
1462
- }
1463
-
1464
- static void
1465
- dump_obj_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
1466
- if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
1467
- volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1468
-
1469
- if (T_HASH != rb_type(h)) {
1470
- // It seems that ActiveRecord implemented to_hash so that it returns
1471
- // an Array and not a Hash. To get around that any value returned
1472
- // will be dumped.
1473
-
1474
- //rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1475
- dump_val(h, depth, out, 0, 0, false);
1476
- } else {
1477
- dump_hash(h, Qundef, depth, out->opts->mode, out);
1478
- }
1479
- } else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
1480
- volatile VALUE aj;
1481
-
1482
- #if HAS_METHOD_ARITY
1483
- // Some classes elect to not take an options argument so check the arity
1484
- // of as_json.
1485
- switch (rb_obj_method_arity(obj, oj_as_json_id)) {
1486
- case 0:
1487
- aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
1488
- break;
1489
- case 1:
1490
- if (1 <= argc) {
1491
- aj = rb_funcall2(obj, oj_as_json_id, 1, argv);
1492
- } else {
1493
- VALUE nothing [1];
1494
-
1495
- nothing[0] = Qnil;
1496
- aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
1497
- }
1498
- break;
1499
- default:
1500
- aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
1501
- break;
1502
- }
1503
- #else
1504
- aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
1505
- #endif
1506
- // Catch the obvious brain damaged recursive dumping.
1507
- if (aj == obj) {
1508
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1509
-
1510
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1511
- } else {
1512
- dump_val(aj, depth, out, 0, 0, false);
1513
- }
1514
- } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
1515
- volatile VALUE rs;
1516
- const char *s;
1517
- int len;
1518
-
1519
- rs = rb_funcall(obj, oj_to_json_id, 0);
1520
- s = rb_string_value_ptr((VALUE*)&rs);
1521
- len = (int)RSTRING_LEN(rs);
1522
-
1523
- if (out->end - out->cur <= len + 1) {
1524
- grow(out, len);
1525
- }
1526
- memcpy(out->cur, s, len);
1527
- out->cur += len;
1528
- *out->cur = '\0';
1529
- } else {
1530
- VALUE clas = rb_obj_class(obj);
1531
-
1532
- if (oj_bigdecimal_class == clas) {
1533
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1534
-
1535
- if (Yes == out->opts->bigdec_as_num) {
1536
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
1537
- } else {
1538
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1539
- }
1540
- #if (defined T_RATIONAL && defined RRATIONAL)
1541
- } else if (oj_datetime_class == clas || oj_date_class == clas || rb_cRational == clas) {
1542
- #else
1543
- } else if (oj_datetime_class == clas || oj_date_class == clas) {
1544
- #endif
1545
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1546
-
1547
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1548
- } else {
1549
- dump_obj_attrs(obj, Qundef, 0, depth, out);
1550
- }
1551
- }
1552
- *out->cur = '\0';
1553
- }
1554
-
1555
- inline static void
1556
- dump_obj_obj(VALUE obj, int depth, Out out) {
1557
- long id = check_circular(obj, out);
1558
-
1559
- if (0 <= id) {
1560
- VALUE clas = rb_obj_class(obj);
1561
-
1562
- if (oj_bigdecimal_class == clas) {
1563
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1564
-
1565
- dump_raw(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), out);
1566
- } else {
1567
- dump_obj_attrs(obj, clas, id, depth, out);
1568
- }
1569
- }
1570
- }
1571
-
1572
- #ifdef RUBINIUS_RUBY
1573
- static int
1574
- isRbxHashAttr(const char *attr) {
1575
- const char *hashAttrs[] = {
1576
- "@capacity",
1577
- "@max_entries",
1578
- "@state",
1579
- "@mask",
1580
- "@size",
1581
- "@entries",
1582
- "@default_proc",
1583
- 0 };
1584
- const char **ap;
1585
-
1586
- for (ap = hashAttrs; 0 != *ap; ap++) {
1587
- if (0 == strcmp(attr, *ap)) {
1588
- return 1;
1589
- }
1590
- }
1591
- return 0;
1592
- }
1593
- #endif
1594
-
1595
- #if HAS_IVAR_HELPERS
1596
- static int
1597
- dump_attr_cb(ID key, VALUE value, Out out) {
1598
- int depth = out->depth;
1599
- size_t size = depth * out->indent + 1;
1600
- const char *attr = rb_id2name(key);
1601
-
1602
- if (out->omit_nil && Qnil == value) {
1603
- return ST_CONTINUE;
1604
- }
1605
- #if HAS_EXCEPTION_MAGIC
1606
- if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
1607
- return ST_CONTINUE;
1608
- }
1609
- #endif
1610
- if (out->end - out->cur <= (long)size) {
1611
- grow(out, size);
1612
- }
1613
- fill_indent(out, depth);
1614
- if ('@' == *attr) {
1615
- attr++;
1616
- dump_cstr(attr, strlen(attr), 0, 0, out);
1617
- } else {
1618
- char buf[32];
1619
-
1620
- *buf = '~';
1621
- strncpy(buf + 1, attr, sizeof(buf) - 2);
1622
- buf[sizeof(buf) - 1] = '\0';
1623
- dump_cstr(buf, strlen(buf), 0, 0, out);
1624
- }
1625
- *out->cur++ = ':';
1626
- dump_val(value, depth, out, 0, 0, true);
1627
- out->depth = depth;
1628
- *out->cur++ = ',';
1629
-
1630
- return ST_CONTINUE;
1631
- }
1632
- #endif
1633
-
1634
- static void
1635
- dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
1636
- size_t size = 0;
1637
- int d2 = depth + 1;
1638
- int type = rb_type(obj);
1639
-
1640
- if (out->end - out->cur <= 2) {
1641
- grow(out, 2);
1642
- }
1643
- *out->cur++ = '{';
1644
- if (Qundef != clas) {
1645
- const char *class_name = rb_class2name(clas);
1646
- int clen = (int)strlen(class_name);
1647
-
1648
- size = d2 * out->indent + clen + 10;
1649
- if (out->end - out->cur <= (long)size) {
1650
- grow(out, size);
1651
- }
1652
- fill_indent(out, d2);
1653
- *out->cur++ = '"';
1654
- *out->cur++ = '^';
1655
- *out->cur++ = 'o';
1656
- *out->cur++ = '"';
1657
- *out->cur++ = ':';
1658
- dump_cstr(class_name, clen, 0, 0, out);
1659
- }
1660
- if (0 < id) {
1661
- size = d2 * out->indent + 16;
1662
- if (out->end - out->cur <= (long)size) {
1663
- grow(out, size);
1664
- }
1665
- *out->cur++ = ',';
1666
- fill_indent(out, d2);
1667
- *out->cur++ = '"';
1668
- *out->cur++ = '^';
1669
- *out->cur++ = 'i';
1670
- *out->cur++ = '"';
1671
- *out->cur++ = ':';
1672
- dump_ulong(id, out);
1673
- }
1674
- switch (type) {
1675
- case T_STRING:
1676
- size = d2 * out->indent + 14;
1677
- if (out->end - out->cur <= (long)size) {
1678
- grow(out, size);
1679
- }
1680
- *out->cur++ = ',';
1681
- fill_indent(out, d2);
1682
- *out->cur++ = '"';
1683
- *out->cur++ = 's';
1684
- *out->cur++ = 'e';
1685
- *out->cur++ = 'l';
1686
- *out->cur++ = 'f';
1687
- *out->cur++ = '"';
1688
- *out->cur++ = ':';
1689
- dump_cstr(rb_string_value_ptr((VALUE*)&obj), (int)RSTRING_LEN(obj), 0, 0, out);
1690
- break;
1691
- case T_ARRAY:
1692
- size = d2 * out->indent + 14;
1693
- if (out->end - out->cur <= (long)size) {
1694
- grow(out, size);
1695
- }
1696
- *out->cur++ = ',';
1697
- fill_indent(out, d2);
1698
- *out->cur++ = '"';
1699
- *out->cur++ = 's';
1700
- *out->cur++ = 'e';
1701
- *out->cur++ = 'l';
1702
- *out->cur++ = 'f';
1703
- *out->cur++ = '"';
1704
- *out->cur++ = ':';
1705
- dump_array(obj, Qundef, depth + 1, out);
1706
- break;
1707
- case T_HASH:
1708
- size = d2 * out->indent + 14;
1709
- if (out->end - out->cur <= (long)size) {
1710
- grow(out, size);
1711
- }
1712
- *out->cur++ = ',';
1713
- fill_indent(out, d2);
1714
- *out->cur++ = '"';
1715
- *out->cur++ = 's';
1716
- *out->cur++ = 'e';
1717
- *out->cur++ = 'l';
1718
- *out->cur++ = 'f';
1719
- *out->cur++ = '"';
1720
- *out->cur++ = ':';
1721
- dump_hash(obj, Qundef, depth + 1, out->opts->mode, out);
1722
- break;
1723
- default:
1724
- break;
1725
- }
1726
- {
1727
- int cnt;
1728
- #if HAS_IVAR_HELPERS
1729
- cnt = (int)rb_ivar_count(obj);
1730
- #else
1731
- volatile VALUE vars = rb_funcall2(obj, oj_instance_variables_id, 0, 0);
1732
- VALUE *np = RARRAY_PTR(vars);
1733
- ID vid;
1734
- const char *attr;
1735
- int i;
1736
- int first = 1;
1737
-
1738
- cnt = (int)RARRAY_LEN(vars);
1739
- #endif
1740
- if (Qundef != clas && 0 < cnt) {
1741
- *out->cur++ = ',';
1742
- }
1743
- out->depth = depth + 1;
1744
- #if HAS_IVAR_HELPERS
1745
- rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
1746
- if (',' == *(out->cur - 1)) {
1747
- out->cur--; // backup to overwrite last comma
1748
- }
1749
- #else
1750
- size = d2 * out->indent + 1;
1751
- for (i = cnt; 0 < i; i--, np++) {
1752
- VALUE value;
1753
-
1754
- vid = rb_to_id(*np);
1755
- attr = rb_id2name(vid);
1756
- #ifdef RUBINIUS_RUBY
1757
- if (T_HASH == type && isRbxHashAttr(attr)) {
1758
- continue;
1759
- }
1760
- #endif
1761
- value = rb_ivar_get(obj, vid);
1762
- if (out->omit_nil && Qnil == value) {
1763
- continue;
1764
- }
1765
- if (first) {
1766
- first = 0;
1767
- } else {
1768
- *out->cur++ = ',';
1769
- }
1770
- if (out->end - out->cur <= (long)size) {
1771
- grow(out, size);
1772
- }
1773
- fill_indent(out, d2);
1774
- if ('@' == *attr) {
1775
- attr++;
1776
- dump_cstr(attr, strlen(attr), 0, 0, out);
1777
- } else {
1778
- char buf[32];
1779
-
1780
- *buf = '~';
1781
- strncpy(buf + 1, attr, sizeof(buf) - 2);
1782
- buf[sizeof(buf) - 1] = '\0';
1783
- dump_cstr(buf, strlen(attr) + 1, 0, 0, out);
1784
- }
1785
- *out->cur++ = ':';
1786
- dump_val(value, d2, out, 0, 0, true);
1787
- if (out->end - out->cur <= 2) {
1788
- grow(out, 2);
1789
- }
1790
- }
1791
- #endif
1792
- #if HAS_EXCEPTION_MAGIC
1793
- if (rb_obj_is_kind_of(obj, rb_eException)) {
1794
- volatile VALUE rv;
1795
-
1796
- if (',' != *(out->cur - 1)) {
1797
- *out->cur++ = ',';
1798
- }
1799
- // message
1800
- if (out->end - out->cur <= (long)size) {
1801
- grow(out, size);
1802
- }
1803
- fill_indent(out, d2);
1804
- dump_cstr("~mesg", 5, 0, 0, out);
1805
- *out->cur++ = ':';
1806
- rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
1807
- dump_val(rv, d2, out, 0, 0, true);
1808
- if (out->end - out->cur <= 2) {
1809
- grow(out, 2);
1810
- }
1811
- *out->cur++ = ',';
1812
- // backtrace
1813
- if (out->end - out->cur <= (long)size) {
1814
- grow(out, size);
1815
- }
1816
- fill_indent(out, d2);
1817
- dump_cstr("~bt", 3, 0, 0, out);
1818
- *out->cur++ = ':';
1819
- rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
1820
- dump_val(rv, d2, out, 0, 0, true);
1821
- if (out->end - out->cur <= 2) {
1822
- grow(out, 2);
1823
- }
1824
- }
1825
- #endif
1826
- out->depth = depth;
1827
- }
1828
- fill_indent(out, depth);
1829
- *out->cur++ = '}';
1830
- *out->cur = '\0';
1831
- }
1832
-
1833
- static void
1834
- dump_struct_comp(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
1835
- if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
1836
- volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
1837
-
1838
- if (T_HASH != rb_type(h)) {
1839
- // It seems that ActiveRecord implemented to_hash so that it returns
1840
- // an Array and not a Hash. To get around that any value returned
1841
- // will be dumped.
1842
-
1843
- //rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
1844
- dump_val(h, depth, out, 0, 0, false);
1845
- }
1846
- dump_hash(h, Qundef, depth, out->opts->mode, out);
1847
- } else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
1848
- volatile VALUE aj;
1849
-
1850
- #if HAS_METHOD_ARITY
1851
- // Some classes elect to not take an options argument so check the arity
1852
- // of as_json.
1853
- switch (rb_obj_method_arity(obj, oj_as_json_id)) {
1854
- case 0:
1855
- aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
1856
- break;
1857
- case 1:
1858
- if (1 <= argc) {
1859
- aj = rb_funcall2(obj, oj_as_json_id, 1, argv);
1860
- } else {
1861
- VALUE nothing [1];
1862
-
1863
- nothing[0] = Qnil;
1864
- aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
1865
- }
1866
- break;
1867
- default:
1868
- aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
1869
- break;
1870
- }
1871
- #else
1872
- aj = rb_funcall2(obj, oj_as_json_id, argc, argv);
1873
- #endif
1874
- // Catch the obvious brain damaged recursive dumping.
1875
- if (aj == obj) {
1876
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1877
-
1878
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1879
- } else {
1880
- dump_val(aj, depth, out, 0, 0, false);
1881
- }
1882
- } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
1883
- volatile VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
1884
- const char *s;
1885
- int len;
1886
-
1887
- s = rb_string_value_ptr((VALUE*)&rs);
1888
- len = (int)RSTRING_LEN(rs);
1889
- if (out->end - out->cur <= len) {
1890
- grow(out, len);
1891
- }
1892
- memcpy(out->cur, s, len);
1893
- out->cur += len;
1894
- } else {
1895
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1896
-
1897
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1898
- }
1899
- }
1900
-
1901
- static void
1902
- dump_struct_obj(VALUE obj, int depth, Out out) {
1903
- VALUE clas = rb_obj_class(obj);
1904
- const char *class_name = rb_class2name(clas);
1905
- int i;
1906
- int d2 = depth + 1;
1907
- int d3 = d2 + 1;
1908
- size_t len = strlen(class_name);
1909
- size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
1910
-
1911
- if (out->end - out->cur <= (long)size) {
1912
- grow(out, size);
1913
- }
1914
- *out->cur++ = '{';
1915
- fill_indent(out, d2);
1916
- *out->cur++ = '"';
1917
- *out->cur++ = '^';
1918
- *out->cur++ = 'u';
1919
- *out->cur++ = '"';
1920
- *out->cur++ = ':';
1921
- *out->cur++ = '[';
1922
- #if HAS_STRUCT_MEMBERS
1923
- if ('#' == *class_name) {
1924
- VALUE ma = rb_struct_s_members(clas);
1925
- const char *name;
1926
- int cnt = (int)RARRAY_LEN(ma);
1927
-
1928
- *out->cur++ = '[';
1929
- for (i = 0; i < cnt; i++) {
1930
- name = rb_id2name(SYM2ID(rb_ary_entry(ma, i)));
1931
- len = strlen(name);
1932
- size = len + 3;
1933
- if (out->end - out->cur <= (long)size) {
1934
- grow(out, size);
1935
- }
1936
- if (0 < i) {
1937
- *out->cur++ = ',';
1938
- }
1939
- *out->cur++ = '"';
1940
- memcpy(out->cur, name, len);
1941
- out->cur += len;
1942
- *out->cur++ = '"';
1943
- }
1944
- *out->cur++ = ']';
1945
- } else {
1946
- #else
1947
- if (true) {
1948
- #endif
1949
- fill_indent(out, d3);
1950
- *out->cur++ = '"';
1951
- memcpy(out->cur, class_name, len);
1952
- out->cur += len;
1953
- *out->cur++ = '"';
1954
- }
1955
- *out->cur++ = ',';
1956
- size = d3 * out->indent + 2;
1957
- #ifdef RSTRUCT_LEN
1958
- {
1959
- VALUE v;
1960
- int cnt;
1961
- #if UNIFY_FIXNUM_AND_BIGNUM
1962
- cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
1963
- #else // UNIFY_FIXNUM_AND_INTEGER
1964
- cnt = (int)RSTRUCT_LEN(obj);
1965
- #endif // UNIFY_FIXNUM_AND_INTEGER
1966
-
1967
- for (i = 0; i < cnt; i++) {
1968
- v = RSTRUCT_GET(obj, i);
1969
- if (out->end - out->cur <= (long)size) {
1970
- grow(out, size);
1971
- }
1972
- fill_indent(out, d3);
1973
- dump_val(v, d3, out, 0, 0, true);
1974
- *out->cur++ = ',';
1975
- }
1976
- }
1977
- #else
1978
- {
1979
- // This is a bit risky as a struct in C ruby is not the same as a Struct
1980
- // class in interpreted Ruby so length() may not be defined.
1981
- int slen = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
1982
-
1983
- for (i = 0; i < slen; i++) {
1984
- if (out->end - out->cur <= (long)size) {
1985
- grow(out, size);
1986
- }
1987
- fill_indent(out, d3);
1988
- dump_val(rb_struct_aref(obj, INT2FIX(i)), d3, out, 0, 0, true);
1989
- *out->cur++ = ',';
1990
- }
1991
- }
1992
- #endif
1993
- out->cur--;
1994
- *out->cur++ = ']';
1995
- *out->cur++ = '}';
1996
- *out->cur = '\0';
1997
- }
1998
-
1999
- static void
2000
- dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
2001
- ID *idp;
2002
- AttrGetFunc *fp;
2003
- volatile VALUE v;
2004
- const char *name;
2005
- size_t size;
2006
- int d2 = depth + 1;
2007
-
2008
- if (out->end - out->cur <= 2) {
2009
- grow(out, 2);
2010
- }
2011
- *out->cur++ = '{';
2012
- if (Qundef != clas) {
2013
- const char *class_name = rb_class2name(clas);
2014
- int clen = (int)strlen(class_name);
2015
-
2016
- size = d2 * out->indent + clen + 10;
2017
- if (out->end - out->cur <= (long)size) {
2018
- grow(out, size);
2019
- }
2020
- fill_indent(out, d2);
2021
- *out->cur++ = '"';
2022
- *out->cur++ = '^';
2023
- *out->cur++ = 'O';
2024
- *out->cur++ = '"';
2025
- *out->cur++ = ':';
2026
- dump_cstr(class_name, clen, 0, 0, out);
2027
- *out->cur++ = ',';
2028
- }
2029
- if (odd->raw) {
2030
- v = rb_funcall(obj, *odd->attrs, 0);
2031
- if (Qundef == v || T_STRING != rb_type(v)) {
2032
- rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
2033
- } else {
2034
- const char *s = rb_string_value_ptr((VALUE*)&v);
2035
- int len = (int)RSTRING_LEN(v);
2036
- const char *name = rb_id2name(*odd->attrs);
2037
- size_t nlen = strlen(name);
2038
-
2039
- size = len + d2 * out->indent + nlen + 10;
2040
- if (out->end - out->cur <= (long)size) {
2041
- grow(out, size);
2042
- }
2043
- fill_indent(out, d2);
2044
- *out->cur++ = '"';
2045
- memcpy(out->cur, name, nlen);
2046
- out->cur += nlen;
2047
- *out->cur++ = '"';
2048
- *out->cur++ = ':';
2049
- memcpy(out->cur, s, len);
2050
- out->cur += len;
2051
- *out->cur = '\0';
2052
- }
2053
- } else {
2054
- size = d2 * out->indent + 1;
2055
- for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
2056
- size_t nlen;
2057
-
2058
- if (out->end - out->cur <= (long)size) {
2059
- grow(out, size);
2060
- }
2061
- name = rb_id2name(*idp);
2062
- nlen = strlen(name);
2063
- if (0 != *fp) {
2064
- v = (*fp)(obj);
2065
- } else if (0 == strchr(name, '.')) {
2066
- v = rb_funcall(obj, *idp, 0);
2067
- } else {
2068
- char nbuf[256];
2069
- char *n2 = nbuf;
2070
- char *n;
2071
- char *end;
2072
- ID i;
2073
-
2074
- if (sizeof(nbuf) <= nlen) {
2075
- n2 = strdup(name);
2076
- } else {
2077
- strcpy(n2, name);
2078
- }
2079
- n = n2;
2080
- v = obj;
2081
- while (0 != (end = strchr(n, '.'))) {
2082
- *end = '\0';
2083
- i = rb_intern(n);
2084
- v = rb_funcall(v, i, 0);
2085
- n = end + 1;
2086
- }
2087
- i = rb_intern(n);
2088
- v = rb_funcall(v, i, 0);
2089
- if (nbuf != n2) {
2090
- free(n2);
2091
- }
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;
438
+ } else {
439
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
440
+ nsec = (nsec + 5) / 10;
441
+ one /= 10;
2092
442
  }
2093
- fill_indent(out, d2);
2094
- dump_cstr(name, nlen, 0, 0, out);
2095
- *out->cur++ = ':';
2096
- dump_val(v, d2, out, 0, 0, true);
2097
- if (out->end - out->cur <= 2) {
2098
- grow(out, 2);
443
+ if (one <= nsec) {
444
+ nsec -= one;
445
+ sec++;
2099
446
  }
2100
- *out->cur++ = ',';
2101
447
  }
2102
- out->cur--;
2103
448
  }
2104
- *out->cur++ = '}';
2105
- *out->cur = '\0';
2106
- }
2107
-
2108
- static void
2109
- raise_strict(VALUE obj) {
2110
- rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.\n", rb_class2name(rb_obj_class(obj)));
2111
- }
2112
-
2113
- static void
2114
- dump_val(VALUE obj, int depth, Out out, int argc, VALUE *argv, bool as_ok) {
2115
- int type = rb_type(obj);
2116
-
2117
- if (MAX_DEPTH < depth) {
2118
- rb_raise(rb_eNoMemError, "Too deeply nested.\n");
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);
461
+ }
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);
2119
470
  }
2120
- #ifdef OJ_DEBUG
2121
- printf("Oj-debug: dump_val %s\n", rb_class2name(rb_obj_class(obj)));
2122
471
  #endif
2123
- switch (type) {
2124
- case T_NIL: dump_nil(out); break;
2125
- case T_TRUE: dump_true(out); break;
2126
- case T_FALSE: dump_false(out); break;
2127
- case T_FIXNUM: dump_fixnum(obj, out); break;
2128
- case T_FLOAT: dump_float(obj, out); break;
2129
- case T_MODULE:
2130
- case T_CLASS:
2131
- switch (out->opts->mode) {
2132
- case StrictMode: raise_strict(obj); break;
2133
- case NullMode: dump_nil(out); break;
2134
- case CompatMode: dump_class_comp(obj, out); break;
2135
- case ObjectMode:
2136
- default: dump_class_obj(obj, out); break;
2137
- }
2138
- break;
2139
- case T_SYMBOL:
2140
- switch (out->opts->mode) {
2141
- case StrictMode: raise_strict(obj); break;
2142
- case NullMode: dump_nil(out); break;
2143
- case CompatMode: dump_sym_comp(obj, out); break;
2144
- case ObjectMode:
2145
- default: dump_sym_obj(obj, out); break;
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);
2146
484
  }
2147
- break;
2148
- case T_STRUCT: // for Range
2149
- switch (out->opts->mode) {
2150
- case StrictMode: raise_strict(obj); break;
2151
- case NullMode: dump_nil(out); break;
2152
- case CompatMode: dump_struct_comp(obj, depth, out, argc, argv, as_ok); break;
2153
- case ObjectMode:
2154
- default: dump_struct_obj(obj, depth, out); break;
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;
488
+
489
+ if (9 > out->opts->sec_prec) {
490
+ format[32] = '0' + out->opts->sec_prec;
491
+ len -= 9 - out->opts->sec_prec;
2155
492
  }
2156
- break;
2157
- default:
2158
- // Most developers have enough sense not to subclass primitive types but
2159
- // since these classes could potentially be subclassed a check for odd
2160
- // classes is performed.
2161
- {
2162
- VALUE clas = rb_obj_class(obj);
2163
- Odd odd;
2164
-
2165
- if (ObjectMode == out->opts->mode && 0 != (odd = oj_get_odd(clas))) {
2166
- dump_odd(obj, odd, clas, depth + 1, out);
2167
- return;
2168
- }
2169
- switch (type) {
2170
- case T_BIGNUM: dump_bignum(obj, out); break;
2171
- case T_STRING:
2172
- switch (out->opts->mode) {
2173
- case StrictMode:
2174
- case NullMode:
2175
- case CompatMode: dump_str_comp(obj, out); break;
2176
- case ObjectMode:
2177
- default: dump_str_obj(obj, clas, depth, out); break;
2178
- }
2179
- break;
2180
- case T_ARRAY: dump_array(obj, clas, depth, out); break;
2181
- case T_HASH: dump_hash(obj, clas, depth, out->opts->mode, out); break;
2182
- #if (defined T_RATIONAL && defined RRATIONAL)
2183
- case T_RATIONAL:
2184
- #endif
2185
- case T_OBJECT:
2186
- switch (out->opts->mode) {
2187
- case StrictMode: dump_data_strict(obj, out); break;
2188
- case NullMode: dump_data_null(obj, out); break;
2189
- case CompatMode: dump_obj_comp(obj, depth, out, argc, argv, as_ok); break;
2190
- case ObjectMode:
2191
- default: dump_obj_obj(obj, depth, out); break;
2192
- }
2193
- break;
2194
- case T_DATA:
2195
- switch (out->opts->mode) {
2196
- case StrictMode: dump_data_strict(obj, out); break;
2197
- case NullMode: dump_data_null(obj, out); break;
2198
- case CompatMode: dump_data_comp(obj, depth, out, argc, argv, as_ok);break;
2199
- case ObjectMode:
2200
- default: dump_data_obj(obj, depth, out); break;
2201
- }
2202
- break;
2203
- #if (defined T_COMPLEX && defined RCOMPLEX)
2204
- case T_COMPLEX:
2205
- #endif
2206
- case T_REGEXP:
2207
- switch (out->opts->mode) {
2208
- case StrictMode: raise_strict(obj); break;
2209
- case NullMode: dump_nil(out); break;
2210
- case CompatMode:
2211
- case ObjectMode:
2212
- default: dump_obj_comp(obj, depth, out, argc, argv, as_ok); break;
2213
- }
2214
- break;
2215
- default:
2216
- switch (out->opts->mode) {
2217
- case StrictMode: raise_strict(obj); break;
2218
- case NullMode: dump_nil(out); break;
2219
- case CompatMode: {
2220
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
2221
- dump_cstr(rb_string_value_ptr((VALUE*)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
2222
- break;
2223
- }
2224
- case ObjectMode:
2225
- default:
2226
- rb_raise(rb_eNotImpError, "Failed to dump '%s' Object (%02x)\n",
2227
- rb_class2name(rb_obj_class(obj)), rb_type(obj));
2228
- break;
2229
- }
2230
- break;
2231
- }
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;
500
+
501
+ if (9 > out->opts->sec_prec) {
502
+ format[32] = '0' + out->opts->sec_prec;
503
+ len -= 9 - out->opts->sec_prec;
2232
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);
2233
510
  }
2234
511
  }
2235
512
 
@@ -2249,16 +526,26 @@ oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VA
2249
526
  out->circ_cnt = 0;
2250
527
  out->opts = copts;
2251
528
  out->hash_cnt = 0;
529
+ out->indent = copts->indent;
530
+ out->argc = argc;
531
+ out->argv = argv;
2252
532
  if (Yes == copts->circular) {
2253
533
  oj_cache8_new(&out->circ_cache);
2254
534
  }
2255
- out->indent = copts->indent;
2256
- dump_val(obj, 0, out, argc, argv, true);
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
+ }
2257
544
  if (0 < out->indent) {
2258
545
  switch (*(out->cur - 1)) {
2259
546
  case ']':
2260
547
  case '}':
2261
- grow(out, 1);
548
+ assure_size(out, 1);
2262
549
  *out->cur++ = '\n';
2263
550
  default:
2264
551
  break;
@@ -2288,7 +575,7 @@ oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
2288
575
  if (out.allocated) {
2289
576
  xfree(out.buf);
2290
577
  }
2291
- rb_raise(rb_eIOError, "%s\n", strerror(errno));
578
+ rb_raise(rb_eIOError, "%s", strerror(errno));
2292
579
  }
2293
580
  ok = (size == fwrite(out.buf, 1, size, f));
2294
581
  if (out.allocated) {
@@ -2298,7 +585,7 @@ oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
2298
585
  if (!ok) {
2299
586
  int err = ferror(f);
2300
587
 
2301
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
588
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
2302
589
  }
2303
590
  }
2304
591
 
@@ -2329,7 +616,7 @@ oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
2329
616
  if (out.allocated) {
2330
617
  xfree(out.buf);
2331
618
  }
2332
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", errno, strerror(errno));
619
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]", errno, strerror(errno));
2333
620
  }
2334
621
  #endif
2335
622
  } else if (rb_respond_to(stream, oj_write_id)) {
@@ -2345,427 +632,422 @@ oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
2345
632
  }
2346
633
  }
2347
634
 
2348
- // dump leaf functions
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));
2349
639
 
2350
- inline static void
2351
- dump_chars(const char *s, size_t size, Out out) {
2352
- if (out->end - out->cur <= (long)size) {
2353
- grow(out, size);
640
+ if (rb_utf8_encoding() != enc) {
641
+ obj = rb_str_conv_enc(obj, enc, rb_utf8_encoding());
2354
642
  }
2355
- memcpy(out->cur, s, size);
2356
- out->cur += size;
2357
- *out->cur = '\0';
643
+ #endif
644
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
2358
645
  }
2359
646
 
2360
- static void
2361
- dump_leaf_str(Leaf leaf, Out out) {
2362
- switch (leaf->value_type) {
2363
- case STR_VAL:
2364
- dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out);
2365
- break;
2366
- case RUBY_VAL:
2367
- dump_cstr(rb_string_value_cstr(&leaf->value), (int)RSTRING_LEN(leaf->value), 0, 0, out);
2368
- break;
2369
- case COL_VAL:
2370
- default:
2371
- rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
2372
- break;
2373
- }
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);
2374
652
  }
2375
653
 
2376
- static void
2377
- dump_leaf_fixnum(Leaf leaf, Out out) {
2378
- switch (leaf->value_type) {
2379
- case STR_VAL:
2380
- dump_chars(leaf->str, strlen(leaf->str), out);
654
+ void
655
+ oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
656
+ size_t size;
657
+ char *cmap;
658
+
659
+ switch (out->opts->escape_mode) {
660
+ case NLEsc:
661
+ cmap = newline_friendly_chars;
662
+ size = newline_friendly_size((uint8_t*)str, cnt);
2381
663
  break;
2382
- case RUBY_VAL:
2383
- if (T_BIGNUM == rb_type(leaf->value)) {
2384
- dump_bignum(leaf->value, out);
2385
- } else {
2386
- dump_fixnum(leaf->value, out);
2387
- }
664
+ case ASCIIEsc:
665
+ cmap = ascii_friendly_chars;
666
+ size = ascii_friendly_size((uint8_t*)str, cnt);
2388
667
  break;
2389
- case COL_VAL:
2390
- default:
2391
- rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
668
+ case XSSEsc:
669
+ cmap = xss_friendly_chars;
670
+ size = xss_friendly_size((uint8_t*)str, cnt);
2392
671
  break;
2393
- }
2394
- }
2395
-
2396
- static void
2397
- dump_leaf_float(Leaf leaf, Out out) {
2398
- switch (leaf->value_type) {
2399
- case STR_VAL:
2400
- dump_chars(leaf->str, strlen(leaf->str), out);
672
+ case JXEsc:
673
+ cmap = hixss_friendly_chars;
674
+ size = hixss_friendly_size((uint8_t*)str, cnt);
2401
675
  break;
2402
- case RUBY_VAL:
2403
- dump_float(leaf->value, out);
676
+ case RailsEsc:
677
+ cmap = rails_friendly_chars;
678
+ size = rails_friendly_size((uint8_t*)str, cnt);
2404
679
  break;
2405
- case COL_VAL:
680
+ case JSONEsc:
2406
681
  default:
2407
- rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
2408
- break;
682
+ cmap = hibit_friendly_chars;
683
+ size = hibit_friendly_size((uint8_t*)str, cnt);
2409
684
  }
2410
- }
2411
-
2412
- static void
2413
- dump_leaf_array(Leaf leaf, int depth, Out out) {
2414
- size_t size;
2415
- int d2 = depth + 1;
685
+ assure_size(out, size + BUFFER_EXTRA);
686
+ *out->cur++ = '"';
2416
687
 
2417
- size = 2;
2418
- if (out->end - out->cur <= (long)size) {
2419
- grow(out, size);
688
+ if (escape1) {
689
+ *out->cur++ = '\\';
690
+ *out->cur++ = 'u';
691
+ *out->cur++ = '0';
692
+ *out->cur++ = '0';
693
+ dump_hex((uint8_t)*str, out);
694
+ cnt--;
695
+ size--;
696
+ str++;
697
+ is_sym = 0; // just to make sure
2420
698
  }
2421
- *out->cur++ = '[';
2422
- if (0 == leaf->elements) {
2423
- *out->cur++ = ']';
2424
- } else {
2425
- Leaf first = leaf->elements->next;
2426
- Leaf e = first;
2427
-
2428
- size = d2 * out->indent + 2;
2429
- do {
2430
- if (out->end - out->cur <= (long)size) {
2431
- grow(out, size);
2432
- }
2433
- fill_indent(out, d2);
2434
- dump_leaf(e, d2, out);
2435
- if (e->next != first) {
2436
- *out->cur++ = ',';
2437
- }
2438
- e = e->next;
2439
- } while (e != first);
2440
- size = depth * out->indent + 1;
2441
- if (out->end - out->cur <= (long)size) {
2442
- grow(out, size);
699
+ if (cnt == size) {
700
+ if (is_sym) {
701
+ *out->cur++ = ':';
2443
702
  }
2444
- fill_indent(out, depth);
2445
- *out->cur++ = ']';
2446
- }
2447
- *out->cur = '\0';
2448
- }
2449
-
2450
- static void
2451
- dump_leaf_hash(Leaf leaf, int depth, Out out) {
2452
- size_t size;
2453
- int d2 = depth + 1;
2454
-
2455
- size = 2;
2456
- if (out->end - out->cur <= (long)size) {
2457
- grow(out, size);
2458
- }
2459
- *out->cur++ = '{';
2460
- if (0 == leaf->elements) {
2461
- *out->cur++ = '}';
703
+ for (; '\0' != *str; str++) {
704
+ *out->cur++ = *str;
705
+ }
706
+ *out->cur++ = '"';
2462
707
  } else {
2463
- Leaf first = leaf->elements->next;
2464
- Leaf e = first;
708
+ const char *end = str + cnt;
2465
709
 
2466
- size = d2 * out->indent + 2;
2467
- do {
2468
- if (out->end - out->cur <= (long)size) {
2469
- grow(out, size);
2470
- }
2471
- fill_indent(out, d2);
2472
- dump_cstr(e->key, strlen(e->key), 0, 0, out);
710
+ if (is_sym) {
2473
711
  *out->cur++ = ':';
2474
- dump_leaf(e, d2, out);
2475
- if (e->next != first) {
2476
- *out->cur++ = ',';
2477
- }
2478
- e = e->next;
2479
- } while (e != first);
2480
- size = depth * out->indent + 1;
2481
- if (out->end - out->cur <= (long)size) {
2482
- grow(out, size);
2483
712
  }
2484
- fill_indent(out, depth);
2485
- *out->cur++ = '}';
2486
- }
2487
- *out->cur = '\0';
2488
- }
2489
-
2490
- static void
2491
- dump_leaf(Leaf leaf, int depth, Out out) {
2492
- switch (leaf->rtype) {
2493
- case T_NIL:
2494
- dump_nil(out);
2495
- break;
2496
- case T_TRUE:
2497
- dump_true(out);
2498
- break;
2499
- case T_FALSE:
2500
- dump_false(out);
2501
- break;
2502
- case T_STRING:
2503
- dump_leaf_str(leaf, out);
2504
- break;
2505
- case T_FIXNUM:
2506
- dump_leaf_fixnum(leaf, out);
2507
- break;
2508
- case T_FLOAT:
2509
- dump_leaf_float(leaf, out);
2510
- break;
2511
- case T_ARRAY:
2512
- dump_leaf_array(leaf, depth, out);
2513
- break;
2514
- case T_HASH:
2515
- dump_leaf_hash(leaf, depth, out);
2516
- break;
2517
- default:
2518
- rb_raise(rb_eTypeError, "Unexpected type %02x.\n", leaf->rtype);
2519
- break;
2520
- }
2521
- }
2522
-
2523
- void
2524
- oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out) {
2525
- if (0 == out->buf) {
2526
- out->buf = ALLOC_N(char, 4096);
2527
- out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
2528
- out->allocated = 1;
2529
- }
2530
- out->cur = out->buf;
2531
- out->circ_cnt = 0;
2532
- out->opts = copts;
2533
- out->hash_cnt = 0;
2534
- out->indent = copts->indent;
2535
- dump_leaf(leaf, 0, out);
2536
- }
2537
-
2538
- void
2539
- oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts) {
2540
- char buf[4096];
2541
- struct _Out out;
2542
- size_t size;
2543
- FILE *f;
2544
-
2545
- out.buf = buf;
2546
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
2547
- out.allocated = 0;
2548
- out.omit_nil = copts->dump_opts.omit_nil;
2549
- oj_dump_leaf_to_json(leaf, copts, &out);
2550
- size = out.cur - out.buf;
2551
- if (0 == (f = fopen(path, "w"))) {
2552
- rb_raise(rb_eIOError, "%s\n", strerror(errno));
2553
- }
2554
- if (size != fwrite(out.buf, 1, size, f)) {
2555
- int err = ferror(f);
2556
-
2557
- rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
713
+ for (; str < end; str++) {
714
+ switch (cmap[(uint8_t)*str]) {
715
+ case '1':
716
+ *out->cur++ = *str;
717
+ break;
718
+ case '2':
719
+ *out->cur++ = '\\';
720
+ switch (*str) {
721
+ case '\\': *out->cur++ = '\\'; break;
722
+ case '\b': *out->cur++ = 'b'; break;
723
+ case '\t': *out->cur++ = 't'; break;
724
+ case '\n': *out->cur++ = 'n'; break;
725
+ case '\f': *out->cur++ = 'f'; break;
726
+ case '\r': *out->cur++ = 'r'; break;
727
+ default: *out->cur++ = *str; break;
728
+ }
729
+ break;
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
+ }
739
+ str = dump_unicode(str, end, out);
740
+ break;
741
+ case '6': // control characters
742
+ *out->cur++ = '\\';
743
+ *out->cur++ = 'u';
744
+ *out->cur++ = '0';
745
+ *out->cur++ = '0';
746
+ dump_hex((uint8_t)*str, out);
747
+ break;
748
+ default:
749
+ break; // ignore, should never happen if the table is correct
750
+ }
751
+ }
752
+ *out->cur++ = '"';
2558
753
  }
2559
- if (out.allocated) {
2560
- xfree(out.buf);
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
+ }
2561
792
  }
2562
- fclose(f);
793
+ *out->cur = '\0';
2563
794
  }
2564
795
 
2565
- // string writer functions
2566
-
2567
- static void
2568
- key_check(StrWriter sw, const char *key) {
2569
- DumpType type = sw->types[sw->depth];
796
+ void
797
+ oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
798
+ const char *s = rb_class2name(obj);
2570
799
 
2571
- if (0 == key && (ObjectNew == type || ObjectType == type)) {
2572
- rb_raise(rb_eStandardError, "Can not push onto an Object without a key.");
2573
- }
800
+ oj_dump_cstr(s, strlen(s), 0, 0, out);
2574
801
  }
2575
802
 
2576
- static void
2577
- push_type(StrWriter sw, DumpType type) {
2578
- if (sw->types_end <= sw->types + sw->depth + 1) {
2579
- size_t size = (sw->types_end - sw->types) * 2;
803
+ void
804
+ oj_dump_obj_to_s(VALUE obj, Out out) {
805
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
2580
806
 
2581
- REALLOC_N(sw->types, char, size);
2582
- sw->types_end = sw->types + size;
2583
- }
2584
- sw->depth++;
2585
- sw->types[sw->depth] = type;
807
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
2586
808
  }
2587
809
 
2588
- static void
2589
- maybe_comma(StrWriter sw) {
2590
- switch (sw->types[sw->depth]) {
2591
- case ObjectNew:
2592
- sw->types[sw->depth] = ObjectType;
2593
- break;
2594
- case ArrayNew:
2595
- sw->types[sw->depth] = ArrayType;
2596
- break;
2597
- case ObjectType:
2598
- case ArrayType:
2599
- // Always have a few characters available in the out.buf.
2600
- *sw->out.cur++ = ',';
2601
- break;
2602
- }
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';
2603
816
  }
2604
817
 
2605
818
  void
2606
- oj_str_writer_push_key(StrWriter sw, const char *key) {
2607
- DumpType type = sw->types[sw->depth];
2608
- long size;
2609
-
2610
- if (sw->keyWritten) {
2611
- rb_raise(rb_eStandardError, "Can not push more than one key before pushing a non-key.");
2612
- }
2613
- if (ObjectNew != type && ObjectType != type) {
2614
- rb_raise(rb_eStandardError, "Can only push a key onto an Object.");
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;
2615
827
  }
2616
- size = sw->depth * sw->out.indent + 3;
2617
- if (sw->out.end - sw->out.cur <= (long)size) {
2618
- grow(&sw->out, size);
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);
2619
834
  }
2620
- maybe_comma(sw);
2621
- if (0 < sw->depth) {
2622
- fill_indent(&sw->out, sw->depth);
835
+ if (0 == buf) {
836
+ rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]", ENOSPC, strerror(ENOSPC));
2623
837
  }
2624
- dump_cstr(key, strlen(key), 0, 0, &sw->out);
2625
- *sw->out.cur++ = ':';
2626
- sw->keyWritten = 1;
838
+ out->buf = buf;
839
+ out->end = buf + size;
840
+ out->cur = out->buf + pos;
2627
841
  }
2628
842
 
2629
843
  void
2630
- oj_str_writer_push_object(StrWriter sw, const char *key) {
2631
- if (sw->keyWritten) {
2632
- sw->keyWritten = 0;
2633
- if (sw->out.end - sw->out.cur <= 1) {
2634
- grow(&sw->out, 1);
2635
- }
2636
- } else {
2637
- long size;
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';
851
+ }
2638
852
 
2639
- key_check(sw, key);
2640
- size = sw->depth * sw->out.indent + 3;
2641
- if (sw->out.end - sw->out.cur <= (long)size) {
2642
- grow(&sw->out, size);
2643
- }
2644
- maybe_comma(sw);
2645
- if (0 < sw->depth) {
2646
- fill_indent(&sw->out, sw->depth);
2647
- }
2648
- if (0 != key) {
2649
- dump_cstr(key, strlen(key), 0, 0, &sw->out);
2650
- *sw->out.cur++ = ':';
2651
- }
2652
- }
2653
- *sw->out.cur++ = '{';
2654
- push_type(sw, ObjectNew);
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';
2655
861
  }
2656
862
 
2657
863
  void
2658
- oj_str_writer_push_array(StrWriter sw, const char *key) {
2659
- if (sw->keyWritten) {
2660
- sw->keyWritten = 0;
2661
- if (sw->out.end - sw->out.cur <= 1) {
2662
- grow(&sw->out, 1);
2663
- }
2664
- } else {
2665
- long size;
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';
871
+ *out->cur = '\0';
872
+ }
2666
873
 
2667
- key_check(sw, key);
2668
- size = sw->depth * sw->out.indent + 3;
2669
- if (sw->out.end - sw->out.cur <= size) {
2670
- grow(&sw->out, size);
2671
- }
2672
- maybe_comma(sw);
2673
- if (0 < sw->depth) {
2674
- fill_indent(&sw->out, sw->depth);
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;
880
+
881
+ if (0 > num) {
882
+ neg = 1;
883
+ num = -num;
884
+ }
885
+ *b-- = '\0';
886
+ if (0 < num) {
887
+ for (; 0 < num; num /= 10, b--) {
888
+ *b = (num % 10) + '0';
2675
889
  }
2676
- if (0 != key) {
2677
- dump_cstr(key, strlen(key), 0, 0, &sw->out);
2678
- *sw->out.cur++ = ':';
890
+ if (neg) {
891
+ *b = '-';
892
+ } else {
893
+ b++;
2679
894
  }
895
+ } else {
896
+ *b = '0';
2680
897
  }
2681
- *sw->out.cur++ = '[';
2682
- push_type(sw, ArrayNew);
898
+ assure_size(out, (sizeof(buf) - (b - buf)));
899
+ for (; '\0' != *b; b++) {
900
+ *out->cur++ = *b;
901
+ }
902
+ *out->cur = '\0';
2683
903
  }
2684
904
 
2685
905
  void
2686
- oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
2687
- if (sw->keyWritten) {
2688
- sw->keyWritten = 0;
2689
- } else {
2690
- long size;
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);
2691
909
 
2692
- key_check(sw, key);
2693
- size = sw->depth * sw->out.indent + 3;
2694
- if (sw->out.end - sw->out.cur <= size) {
2695
- grow(&sw->out, size);
2696
- }
2697
- maybe_comma(sw);
2698
- if (0 < sw->depth) {
2699
- fill_indent(&sw->out, sw->depth);
2700
- }
2701
- if (0 != key) {
2702
- dump_cstr(key, strlen(key), 0, 0, &sw->out);
2703
- *sw->out.cur++ = ':';
2704
- }
2705
- }
2706
- dump_val(val, sw->depth, &sw->out, 0, 0, true);
910
+ assure_size(out, cnt);
911
+ memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
912
+ out->cur += cnt;
913
+ *out->cur = '\0';
2707
914
  }
2708
915
 
916
+ // Removed dependencies on math due to problems with CentOS 5.4.
2709
917
  void
2710
- oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
2711
- if (sw->keyWritten) {
2712
- sw->keyWritten = 0;
2713
- } else {
2714
- long size;
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;
2715
923
 
2716
- key_check(sw, key);
2717
- size = sw->depth * sw->out.indent + 3;
2718
- if (sw->out.end - sw->out.cur <= size) {
2719
- grow(&sw->out, size);
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;
935
+ } else {
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;
945
+ }
946
+ }
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;
964
+ }
2720
965
  }
2721
- maybe_comma(sw);
2722
- if (0 < sw->depth) {
2723
- fill_indent(&sw->out, sw->depth);
966
+ } else if (-OJ_INFINITY == d) {
967
+ if (ObjectMode == out->opts->mode) {
968
+ strcpy(buf, ninf_val);
969
+ cnt = sizeof(ninf_val) - 1;
970
+ } else {
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;
979
+ }
980
+ }
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;
998
+ }
2724
999
  }
2725
- if (0 != key) {
2726
- dump_cstr(key, strlen(key), 0, 0, &sw->out);
2727
- *sw->out.cur++ = ':';
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
+ }
2728
1033
  }
2729
- }
2730
- dump_raw(json, strlen(json), &sw->out);
2731
- }
2732
-
2733
- void
2734
- oj_str_writer_pop(StrWriter sw) {
2735
- long size;
2736
- DumpType type = sw->types[sw->depth];
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);
2737
1038
 
2738
- if (sw->keyWritten) {
2739
- sw->keyWritten = 0;
2740
- rb_raise(rb_eStandardError, "Can not pop after writing a key but no value.");
2741
- }
2742
- sw->depth--;
2743
- if (0 > sw->depth) {
2744
- rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
2745
- }
2746
- size = sw->depth * sw->out.indent + 2;
2747
- if (sw->out.end - sw->out.cur <= size) {
2748
- grow(&sw->out, size);
2749
- }
2750
- fill_indent(&sw->out, sw->depth);
2751
- switch (type) {
2752
- case ObjectNew:
2753
- case ObjectType:
2754
- *sw->out.cur++ = '}';
2755
- break;
2756
- case ArrayNew:
2757
- case ArrayType:
2758
- *sw->out.cur++ = ']';
2759
- break;
2760
- }
2761
- if (0 == sw->depth && 0 <= sw->out.indent) {
2762
- *sw->out.cur++ = '\n';
1039
+ cnt = RSTRING_LEN(rstr);
1040
+ if ((int)sizeof(buf) <= cnt) {
1041
+ cnt = sizeof(buf) - 1;
1042
+ }
1043
+ strncpy(buf, rb_string_value_ptr((VALUE*)&rstr), cnt);
1044
+ buf[cnt] = '\0';
1045
+ } else {
1046
+ cnt = snprintf(buf, sizeof(buf), out->opts->float_fmt, d);
2763
1047
  }
2764
- }
2765
-
2766
- void
2767
- oj_str_writer_pop_all(StrWriter sw) {
2768
- while (0 < sw->depth) {
2769
- oj_str_writer_pop(sw);
1048
+ assure_size(out, cnt);
1049
+ for (b = buf; '\0' != *b; b++) {
1050
+ *out->cur++ = *b;
2770
1051
  }
1052
+ *out->cur = '\0';
2771
1053
  }