oj 2.18.5 → 3.0.0

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