oj 3.11.5 → 3.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1421 -0
  3. data/README.md +19 -5
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +20 -6
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +10 -9
  9. data/ext/oj/circarray.c +8 -6
  10. data/ext/oj/circarray.h +2 -2
  11. data/ext/oj/code.c +19 -33
  12. data/ext/oj/code.h +2 -2
  13. data/ext/oj/compat.c +27 -77
  14. data/ext/oj/custom.c +86 -179
  15. data/ext/oj/debug.c +126 -0
  16. data/ext/oj/dump.c +256 -249
  17. data/ext/oj/dump.h +26 -12
  18. data/ext/oj/dump_compat.c +565 -642
  19. data/ext/oj/dump_leaf.c +17 -63
  20. data/ext/oj/dump_object.c +65 -187
  21. data/ext/oj/dump_strict.c +27 -51
  22. data/ext/oj/encoder.c +43 -0
  23. data/ext/oj/err.c +2 -13
  24. data/ext/oj/err.h +24 -8
  25. data/ext/oj/extconf.rb +21 -6
  26. data/ext/oj/fast.c +149 -149
  27. data/ext/oj/intern.c +313 -0
  28. data/ext/oj/intern.h +22 -0
  29. data/ext/oj/mem.c +318 -0
  30. data/ext/oj/mem.h +53 -0
  31. data/ext/oj/mimic_json.c +121 -106
  32. data/ext/oj/object.c +85 -162
  33. data/ext/oj/odd.c +89 -67
  34. data/ext/oj/odd.h +15 -15
  35. data/ext/oj/oj.c +542 -411
  36. data/ext/oj/oj.h +99 -73
  37. data/ext/oj/parse.c +175 -187
  38. data/ext/oj/parse.h +26 -24
  39. data/ext/oj/parser.c +1600 -0
  40. data/ext/oj/parser.h +101 -0
  41. data/ext/oj/rails.c +112 -159
  42. data/ext/oj/rails.h +1 -1
  43. data/ext/oj/reader.c +11 -14
  44. data/ext/oj/reader.h +4 -2
  45. data/ext/oj/resolve.c +5 -24
  46. data/ext/oj/rxclass.c +7 -6
  47. data/ext/oj/rxclass.h +1 -1
  48. data/ext/oj/saj.c +22 -33
  49. data/ext/oj/saj2.c +584 -0
  50. data/ext/oj/saj2.h +23 -0
  51. data/ext/oj/scp.c +5 -28
  52. data/ext/oj/sparse.c +28 -72
  53. data/ext/oj/stream_writer.c +50 -40
  54. data/ext/oj/strict.c +56 -61
  55. data/ext/oj/string_writer.c +72 -39
  56. data/ext/oj/trace.h +31 -4
  57. data/ext/oj/usual.c +1218 -0
  58. data/ext/oj/usual.h +69 -0
  59. data/ext/oj/util.h +1 -1
  60. data/ext/oj/val_stack.c +14 -3
  61. data/ext/oj/val_stack.h +8 -7
  62. data/ext/oj/validate.c +46 -0
  63. data/ext/oj/wab.c +63 -88
  64. data/lib/oj/active_support_helper.rb +1 -3
  65. data/lib/oj/bag.rb +7 -1
  66. data/lib/oj/easy_hash.rb +4 -5
  67. data/lib/oj/error.rb +1 -2
  68. data/lib/oj/json.rb +162 -150
  69. data/lib/oj/mimic.rb +9 -7
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/schandler.rb +5 -4
  72. data/lib/oj/state.rb +12 -8
  73. data/lib/oj/version.rb +1 -2
  74. data/lib/oj.rb +2 -0
  75. data/pages/Compatibility.md +1 -1
  76. data/pages/InstallOptions.md +20 -0
  77. data/pages/JsonGem.md +15 -0
  78. data/pages/Modes.md +8 -3
  79. data/pages/Options.md +43 -5
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +14 -2
  82. data/test/_test_active.rb +8 -9
  83. data/test/_test_active_mimic.rb +7 -8
  84. data/test/_test_mimic_rails.rb +17 -20
  85. data/test/activerecord/result_test.rb +5 -6
  86. data/test/activesupport6/encoding_test.rb +63 -28
  87. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  88. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  89. data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
  90. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  91. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  92. data/test/files.rb +15 -15
  93. data/test/foo.rb +16 -45
  94. data/test/helper.rb +11 -8
  95. data/test/isolated/shared.rb +3 -2
  96. data/test/json_gem/json_addition_test.rb +2 -2
  97. data/test/json_gem/json_common_interface_test.rb +8 -6
  98. data/test/json_gem/json_encoding_test.rb +0 -0
  99. data/test/json_gem/json_ext_parser_test.rb +1 -0
  100. data/test/json_gem/json_fixtures_test.rb +3 -2
  101. data/test/json_gem/json_generator_test.rb +56 -38
  102. data/test/json_gem/json_generic_object_test.rb +11 -11
  103. data/test/json_gem/json_parser_test.rb +54 -47
  104. data/test/json_gem/json_string_matching_test.rb +9 -9
  105. data/test/json_gem/test_helper.rb +7 -3
  106. data/test/mem.rb +34 -0
  107. data/test/perf.rb +22 -27
  108. data/test/perf_compat.rb +31 -33
  109. data/test/perf_dump.rb +50 -0
  110. data/test/perf_fast.rb +80 -82
  111. data/test/perf_file.rb +27 -29
  112. data/test/perf_object.rb +65 -69
  113. data/test/perf_once.rb +59 -0
  114. data/test/perf_parser.rb +183 -0
  115. data/test/perf_saj.rb +46 -54
  116. data/test/perf_scp.rb +58 -69
  117. data/test/perf_simple.rb +41 -39
  118. data/test/perf_strict.rb +74 -82
  119. data/test/perf_wab.rb +67 -69
  120. data/test/prec.rb +5 -5
  121. data/test/sample/change.rb +0 -1
  122. data/test/sample/dir.rb +0 -1
  123. data/test/sample/doc.rb +0 -1
  124. data/test/sample/file.rb +0 -1
  125. data/test/sample/group.rb +0 -1
  126. data/test/sample/hasprops.rb +0 -1
  127. data/test/sample/layer.rb +0 -1
  128. data/test/sample/rect.rb +0 -1
  129. data/test/sample/shape.rb +0 -1
  130. data/test/sample/text.rb +0 -1
  131. data/test/sample.rb +16 -16
  132. data/test/sample_json.rb +8 -8
  133. data/test/test_compat.rb +95 -43
  134. data/test/test_custom.rb +73 -51
  135. data/test/test_debian.rb +7 -10
  136. data/test/test_fast.rb +135 -79
  137. data/test/test_file.rb +41 -30
  138. data/test/test_gc.rb +16 -5
  139. data/test/test_generate.rb +5 -5
  140. data/test/test_hash.rb +5 -5
  141. data/test/test_integer_range.rb +9 -9
  142. data/test/test_null.rb +20 -20
  143. data/test/test_object.rb +99 -96
  144. data/test/test_parser.rb +11 -0
  145. data/test/test_parser_debug.rb +27 -0
  146. data/test/test_parser_saj.rb +337 -0
  147. data/test/test_parser_usual.rb +251 -0
  148. data/test/test_rails.rb +2 -2
  149. data/test/test_saj.rb +10 -8
  150. data/test/test_scp.rb +37 -39
  151. data/test/test_strict.rb +40 -32
  152. data/test/test_various.rb +165 -84
  153. data/test/test_wab.rb +48 -44
  154. data/test/test_writer.rb +47 -47
  155. data/test/tests.rb +13 -5
  156. data/test/tests_mimic.rb +12 -3
  157. data/test/tests_mimic_addition.rb +12 -3
  158. metadata +74 -128
  159. data/ext/oj/hash.c +0 -131
  160. data/ext/oj/hash.h +0 -19
  161. data/ext/oj/hash_test.c +0 -491
  162. data/test/activesupport4/decoding_test.rb +0 -108
  163. data/test/activesupport4/encoding_test.rb +0 -531
  164. data/test/activesupport4/test_helper.rb +0 -41
  165. data/test/activesupport5/test_helper.rb +0 -72
  166. data/test/bar.rb +0 -35
  167. data/test/baz.rb +0 -16
  168. data/test/zoo.rb +0 -13
data/ext/oj/dump.c CHANGED
@@ -10,8 +10,12 @@
10
10
  #include <stdlib.h>
11
11
  #include <string.h>
12
12
  #include <unistd.h>
13
+ #if !IS_WINDOWS
14
+ #include <poll.h>
15
+ #endif
13
16
 
14
17
  #include "cache8.h"
18
+ #include "mem.h"
15
19
  #include "odd.h"
16
20
  #include "oj.h"
17
21
  #include "trace.h"
@@ -29,6 +33,7 @@ static const char nan_val[] = NAN_VAL;
29
33
  typedef unsigned long ulong;
30
34
 
31
35
  static size_t hibit_friendly_size(const uint8_t *str, size_t len);
36
+ static size_t slash_friendly_size(const uint8_t *str, size_t len);
32
37
  static size_t xss_friendly_size(const uint8_t *str, size_t len);
33
38
  static size_t ascii_friendly_size(const uint8_t *str, size_t len);
34
39
 
@@ -56,6 +61,17 @@ static char hibit_friendly_chars[256] = "\
56
61
  11111111111111111111111111111111\
57
62
  11111111111111111111111111111111";
58
63
 
64
+ // JSON standard but escape forward slashes `/`
65
+ static char slash_friendly_chars[256] = "\
66
+ 66666666222622666666666666666666\
67
+ 11211111111111121111111111111111\
68
+ 11111111111111111111111111112111\
69
+ 11111111111111111111111111111111\
70
+ 11111111111111111111111111111111\
71
+ 11111111111111111111111111111111\
72
+ 11111111111111111111111111111111\
73
+ 11111111111111111111111111111111";
74
+
59
75
  // High bit set characters are always encoded as unicode. Worse case is 3
60
76
  // bytes per character in the output. That makes this conservative.
61
77
  static char ascii_friendly_chars[256] = "\
@@ -113,49 +129,43 @@ static char rails_friendly_chars[256] = "\
113
129
  11111111111111111111111111111111";
114
130
 
115
131
  static void raise_strict(VALUE obj) {
116
- rb_raise(rb_eTypeError,
117
- "Failed to dump %s Object to JSON in strict mode.",
118
- rb_class2name(rb_obj_class(obj)));
132
+ rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.", rb_class2name(rb_obj_class(obj)));
119
133
  }
120
134
 
121
- inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
135
+ inline static size_t calculate_string_size(const uint8_t *str, size_t len, const char *table) {
122
136
  size_t size = 0;
123
137
  size_t i = len;
124
138
 
125
- for (; 0 < i; str++, i--) {
126
- size += newline_friendly_chars[*str];
139
+ for (; 3 < i; i -= 4) {
140
+ size += table[*str++];
141
+ size += table[*str++];
142
+ size += table[*str++];
143
+ size += table[*str++];
144
+ }
145
+ for (; 0 < i; i--) {
146
+ size += table[*str++];
127
147
  }
128
148
  return size - len * (size_t)'0';
129
149
  }
130
150
 
151
+ inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
152
+ return calculate_string_size(str, len, newline_friendly_chars);
153
+ }
154
+
131
155
  inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
132
- size_t size = 0;
133
- size_t i = len;
156
+ return calculate_string_size(str, len, hibit_friendly_chars);
157
+ }
134
158
 
135
- for (; 0 < i; str++, i--) {
136
- size += hibit_friendly_chars[*str];
137
- }
138
- return size - len * (size_t)'0';
159
+ inline static size_t slash_friendly_size(const uint8_t *str, size_t len) {
160
+ return calculate_string_size(str, len, slash_friendly_chars);
139
161
  }
140
162
 
141
163
  inline static size_t ascii_friendly_size(const uint8_t *str, size_t len) {
142
- size_t size = 0;
143
- size_t i = len;
144
-
145
- for (; 0 < i; str++, i--) {
146
- size += ascii_friendly_chars[*str];
147
- }
148
- return size - len * (size_t)'0';
164
+ return calculate_string_size(str, len, ascii_friendly_chars);
149
165
  }
150
166
 
151
167
  inline static size_t xss_friendly_size(const uint8_t *str, size_t len) {
152
- size_t size = 0;
153
- size_t i = len;
154
-
155
- for (; 0 < i; str++, i--) {
156
- size += xss_friendly_chars[*str];
157
- }
158
- return size - len * (size_t)'0';
168
+ return calculate_string_size(str, len, xss_friendly_chars);
159
169
  }
160
170
 
161
171
  inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
@@ -188,13 +198,18 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
188
198
  }
189
199
 
190
200
  inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
191
- size_t size = 0;
192
- size_t i = len;
201
+ long size = 0;
202
+ size_t i = len;
203
+ uint8_t hi = 0;
193
204
 
194
205
  for (; 0 < i; str++, i--) {
195
206
  size += rails_friendly_chars[*str];
207
+ hi |= *str & 0x80;
196
208
  }
197
- return size - len * (size_t)'0';
209
+ if (0 == hi) {
210
+ return size - len * (size_t)'0';
211
+ }
212
+ return -(size - len * (size_t)'0');
198
213
  }
199
214
 
200
215
  const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
@@ -247,7 +262,7 @@ inline static void dump_hex(uint8_t c, Out out) {
247
262
  static void raise_invalid_unicode(const char *str, int len, int pos) {
248
263
  char c;
249
264
  char code[32];
250
- char * cp = code;
265
+ char *cp = code;
251
266
  int i;
252
267
  uint8_t d;
253
268
 
@@ -302,16 +317,14 @@ static const char *dump_unicode(const char *str, const char *end, Out out, const
302
317
  uint32_t c1;
303
318
 
304
319
  code -= 0x00010000;
305
- c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
306
- code = (code & 0x000003FF) + 0x0000DC00;
307
- *out->cur++ = '\\';
308
- *out->cur++ = 'u';
320
+ c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
321
+ code = (code & 0x000003FF) + 0x0000DC00;
322
+ APPEND_CHARS(out->cur, "\\u", 2);
309
323
  for (i = 3; 0 <= i; i--) {
310
324
  *out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
311
325
  }
312
326
  }
313
- *out->cur++ = '\\';
314
- *out->cur++ = 'u';
327
+ APPEND_CHARS(out->cur, "\\u", 2);
315
328
  for (i = 3; 0 <= i; i--) {
316
329
  *out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
317
330
  }
@@ -360,9 +373,7 @@ long oj_check_circular(VALUE obj, Out out) {
360
373
  } else {
361
374
  if (ObjectMode == out->opts->mode) {
362
375
  assure_size(out, 18);
363
- *out->cur++ = '"';
364
- *out->cur++ = '^';
365
- *out->cur++ = 'r';
376
+ APPEND_CHARS(out->cur, "\"^r", 3);
366
377
  dump_ulong(id, out);
367
378
  *out->cur++ = '"';
368
379
  }
@@ -374,15 +385,14 @@ long oj_check_circular(VALUE obj, Out out) {
374
385
 
375
386
  void oj_dump_time(VALUE obj, Out out, int withZone) {
376
387
  char buf[64];
377
- char * b = buf + sizeof(buf) - 1;
388
+ char *b = buf + sizeof(buf) - 1;
378
389
  long size;
379
- char * dot;
390
+ char *dot;
380
391
  int neg = 0;
381
392
  long one = 1000000000;
382
393
  long long sec;
383
394
  long long nsec;
384
395
 
385
- #ifdef HAVE_RB_TIME_TIMESPEC
386
396
  // rb_time_timespec as well as rb_time_timeeval have a bug that causes an
387
397
  // exception to be raised if a time is before 1970 on 32 bit systems so
388
398
  // check the timespec size and use the ruby calls if a 32 bit system.
@@ -392,13 +402,9 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
392
402
  sec = (long long)ts.tv_sec;
393
403
  nsec = ts.tv_nsec;
394
404
  } else {
395
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
396
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
405
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
406
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
397
407
  }
398
- #else
399
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
400
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
401
- #endif
402
408
 
403
409
  *b-- = '\0';
404
410
  if (withZone) {
@@ -464,15 +470,14 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
464
470
  b++;
465
471
  size = sizeof(buf) - (b - buf) - 1;
466
472
  assure_size(out, size);
467
- memcpy(out->cur, b, size);
468
- out->cur += size;
473
+ APPEND_CHARS(out->cur, b, size);
469
474
  *out->cur = '\0';
470
475
  }
471
476
 
472
477
  void oj_dump_ruby_time(VALUE obj, Out out) {
473
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
478
+ volatile VALUE rstr = oj_safe_string_convert(obj);
474
479
 
475
- oj_dump_cstr(rb_string_value_ptr((VALUE *)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
480
+ oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
476
481
  }
477
482
 
478
483
  void oj_dump_xml_time(VALUE obj, Out out) {
@@ -485,20 +490,15 @@ void oj_dump_xml_time(VALUE obj, Out out) {
485
490
  int tzhour, tzmin;
486
491
  char tzsign = '+';
487
492
 
488
- #ifdef HAVE_RB_TIME_TIMESPEC
489
493
  if (16 <= sizeof(struct timespec)) {
490
494
  struct timespec ts = rb_time_timespec(obj);
491
495
 
492
496
  sec = ts.tv_sec;
493
497
  nsec = ts.tv_nsec;
494
498
  } else {
495
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
496
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
499
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
500
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
497
501
  }
498
- #else
499
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
500
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
501
- #endif
502
502
 
503
503
  assure_size(out, 36);
504
504
  if (9 > out->opts->sec_prec) {
@@ -535,59 +535,39 @@ void oj_dump_xml_time(VALUE obj, Out out) {
535
535
  }
536
536
  if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
537
537
  if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
538
- sprintf(buf,
539
- "%04d-%02d-%02dT%02d:%02d:%02dZ",
540
- ti.year,
541
- ti.mon,
542
- ti.day,
543
- ti.hour,
544
- ti.min,
545
- ti.sec);
546
- oj_dump_cstr(buf, 20, 0, 0, out);
538
+ int len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
539
+ oj_dump_cstr(buf, len, 0, 0, out);
547
540
  } else {
548
- sprintf(buf,
549
- "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
550
- ti.year,
551
- ti.mon,
552
- ti.day,
553
- ti.hour,
554
- ti.min,
555
- ti.sec,
556
- tzsign,
557
- tzhour,
558
- tzmin);
559
- oj_dump_cstr(buf, 25, 0, 0, out);
541
+ int len = sprintf(buf,
542
+ "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
543
+ ti.year,
544
+ ti.mon,
545
+ ti.day,
546
+ ti.hour,
547
+ ti.min,
548
+ ti.sec,
549
+ tzsign,
550
+ tzhour,
551
+ tzmin);
552
+ oj_dump_cstr(buf, len, 0, 0, out);
560
553
  }
561
554
  } else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
562
555
  char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
563
- int len = 30;
556
+ int len;
564
557
 
565
558
  if (9 > out->opts->sec_prec) {
566
559
  format[32] = '0' + out->opts->sec_prec;
567
- len -= 9 - out->opts->sec_prec;
568
560
  }
569
- sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
561
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
570
562
  oj_dump_cstr(buf, len, 0, 0, out);
571
563
  } else {
572
564
  char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
573
- int len = 35;
565
+ int len;
574
566
 
575
567
  if (9 > out->opts->sec_prec) {
576
568
  format[32] = '0' + out->opts->sec_prec;
577
- len -= 9 - out->opts->sec_prec;
578
569
  }
579
- sprintf(buf,
580
- format,
581
- ti.year,
582
- ti.mon,
583
- ti.day,
584
- ti.hour,
585
- ti.min,
586
- ti.sec,
587
- (long)nsec,
588
- tzsign,
589
- tzhour,
590
- tzmin);
570
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
591
571
  oj_dump_cstr(buf, len, 0, 0, out);
592
572
  }
593
573
  }
@@ -598,12 +578,8 @@ void oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
598
578
 
599
579
  void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
600
580
  if (0 == out->buf) {
601
- out->buf = ALLOC_N(char, 4096);
602
- // 1 less than end plus extra for possible errors
603
- out->end = out->buf + 4095 - BUFFER_EXTRA;
604
- out->allocated = true;
581
+ oj_out_init(out);
605
582
  }
606
- out->cur = out->buf;
607
583
  out->circ_cnt = 0;
608
584
  out->opts = copts;
609
585
  out->hash_cnt = 0;
@@ -638,38 +614,51 @@ void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int arg
638
614
  }
639
615
 
640
616
  void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
641
- char buf[4096];
642
617
  struct _out out;
643
618
  size_t size;
644
- FILE * f;
619
+ FILE *f;
645
620
  int ok;
646
621
 
647
- out.buf = buf;
648
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
649
- out.allocated = false;
650
- out.omit_nil = copts->dump_opts.omit_nil;
622
+ oj_out_init(&out);
623
+
624
+ out.omit_nil = copts->dump_opts.omit_nil;
651
625
  oj_dump_obj_to_json(obj, copts, &out);
652
626
  size = out.cur - out.buf;
653
627
  if (0 == (f = fopen(path, "w"))) {
654
- if (out.allocated) {
655
- xfree(out.buf);
656
- }
628
+ oj_out_free(&out);
657
629
  rb_raise(rb_eIOError, "%s", strerror(errno));
658
630
  }
659
631
  ok = (size == fwrite(out.buf, 1, size, f));
660
- if (out.allocated) {
661
- xfree(out.buf);
662
- }
663
- fclose(f);
632
+
633
+ oj_out_free(&out);
634
+
664
635
  if (!ok) {
665
636
  int err = ferror(f);
637
+ fclose(f);
666
638
 
667
639
  rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
668
640
  }
641
+ fclose(f);
642
+ }
643
+
644
+ #if !IS_WINDOWS
645
+ static void write_ready(int fd) {
646
+ struct pollfd pp;
647
+ int i;
648
+
649
+ pp.fd = fd;
650
+ pp.events = POLLERR | POLLOUT;
651
+ pp.revents = 0;
652
+ if (0 >= (i = poll(&pp, 1, 5000))) {
653
+ if (0 == i || EAGAIN == errno) {
654
+ rb_raise(rb_eIOError, "write timed out");
655
+ }
656
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
657
+ }
669
658
  }
659
+ #endif
670
660
 
671
661
  void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
672
- char buf[4096];
673
662
  struct _out out;
674
663
  ssize_t size;
675
664
  VALUE clas = rb_obj_class(stream);
@@ -678,58 +667,62 @@ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
678
667
  VALUE s;
679
668
  #endif
680
669
 
681
- out.buf = buf;
682
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
683
- out.allocated = false;
684
- out.omit_nil = copts->dump_opts.omit_nil;
670
+ oj_out_init(&out);
671
+
672
+ out.omit_nil = copts->dump_opts.omit_nil;
685
673
  oj_dump_obj_to_json(obj, copts, &out);
686
674
  size = out.cur - out.buf;
687
675
  if (oj_stringio_class == clas) {
688
676
  rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
689
677
  #if !IS_WINDOWS
690
- } else if (rb_respond_to(stream, oj_fileno_id) &&
691
- Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) && 0 != (fd = FIX2INT(s))) {
692
- if (size != write(fd, out.buf, size)) {
693
- if (out.allocated) {
694
- xfree(out.buf);
678
+ } else if (rb_respond_to(stream, oj_fileno_id) && Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
679
+ 0 != (fd = FIX2INT(s))) {
680
+ ssize_t cnt;
681
+ ssize_t total = 0;
682
+
683
+ while (true) {
684
+ if (0 > (cnt = write(fd, out.buf + total, size - total))) {
685
+ if (EAGAIN != errno) {
686
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
687
+ break;
688
+ }
695
689
  }
696
- rb_raise(rb_eIOError, "Write failed. [%d:%s]", errno, strerror(errno));
690
+ total += cnt;
691
+ if (size <= total) {
692
+ // Completed
693
+ break;
694
+ }
695
+ write_ready(fd);
697
696
  }
698
697
  #endif
699
698
  } else if (rb_respond_to(stream, oj_write_id)) {
700
699
  rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
701
700
  } else {
702
- if (out.allocated) {
703
- xfree(out.buf);
704
- }
701
+ oj_out_free(&out);
705
702
  rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
706
703
  }
707
- if (out.allocated) {
708
- xfree(out.buf);
709
- }
704
+ oj_out_free(&out);
710
705
  }
711
706
 
712
707
  void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
713
- rb_encoding *enc = rb_to_encoding(rb_obj_encoding(obj));
708
+ int idx = RB_ENCODING_GET(obj);
714
709
 
715
- if (rb_utf8_encoding() != enc) {
716
- obj = rb_str_conv_enc(obj, enc, rb_utf8_encoding());
710
+ if (oj_utf8_encoding_index != idx) {
711
+ rb_encoding *enc = rb_enc_from_index(idx);
712
+ obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
717
713
  }
718
- oj_dump_cstr(rb_string_value_ptr((VALUE *)&obj), (int)RSTRING_LEN(obj), 0, 0, out);
714
+ oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
719
715
  }
720
716
 
721
717
  void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
722
- // This causes a memory leak in 2.5.1. Maybe in other versions as well.
723
- // const char *sym = rb_id2name(SYM2ID(obj));
724
-
725
- volatile VALUE s = rb_sym_to_s(obj);
718
+ volatile VALUE s = rb_sym2str(obj);
726
719
 
727
- oj_dump_cstr(rb_string_value_ptr((VALUE *)&s), (int)RSTRING_LEN(s), 0, 0, out);
720
+ oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 0, 0, out);
728
721
  }
729
722
 
730
723
  static void debug_raise(const char *orig, size_t cnt, int line) {
731
724
  char buf[1024];
732
- char * b = buf;
725
+ char *b = buf;
733
726
  const char *s = orig;
734
727
  const char *s_end = s + cnt;
735
728
 
@@ -745,8 +738,11 @@ static void debug_raise(const char *orig, size_t cnt, int line) {
745
738
 
746
739
  void oj_dump_raw_json(VALUE obj, int depth, Out out) {
747
740
  if (oj_string_writer_class == rb_obj_class(obj)) {
748
- StrWriter sw = (StrWriter)DATA_PTR(obj);
749
- size_t len = sw->out.cur - sw->out.buf;
741
+ StrWriter sw;
742
+ size_t len;
743
+
744
+ sw = oj_str_writer_unwrap(obj);
745
+ len = sw->out.cur - sw->out.buf;
750
746
 
751
747
  if (0 < len) {
752
748
  len--;
@@ -755,22 +751,19 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
755
751
  } else {
756
752
  volatile VALUE jv;
757
753
 
758
- if (Yes == out->opts->trace) {
759
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
760
- }
754
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyIn);
761
755
  jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
762
- if (Yes == out->opts->trace) {
763
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
764
- }
765
- oj_dump_raw(rb_string_value_ptr((VALUE *)&jv), (size_t)RSTRING_LEN(jv), out);
756
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyOut);
757
+ oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
766
758
  }
767
759
  }
768
760
 
769
761
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
770
762
  size_t size;
771
- char * cmap;
772
- const char *orig = str;
773
- bool has_hi = false;
763
+ char *cmap;
764
+ const char *orig = str;
765
+ bool has_hi = false;
766
+ bool do_unicode_validation = false;
774
767
 
775
768
  switch (out->opts->escape_mode) {
776
769
  case NLEsc:
@@ -781,13 +774,19 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
781
774
  cmap = ascii_friendly_chars;
782
775
  size = ascii_friendly_size((uint8_t *)str, cnt);
783
776
  break;
777
+ case SlashEsc:
778
+ has_hi = true;
779
+ cmap = slash_friendly_chars;
780
+ size = slash_friendly_size((uint8_t *)str, cnt);
781
+ break;
784
782
  case XSSEsc:
785
783
  cmap = xss_friendly_chars;
786
784
  size = xss_friendly_size((uint8_t *)str, cnt);
787
785
  break;
788
786
  case JXEsc:
789
- cmap = hixss_friendly_chars;
790
- size = hixss_friendly_size((uint8_t *)str, cnt);
787
+ cmap = hixss_friendly_chars;
788
+ size = hixss_friendly_size((uint8_t *)str, cnt);
789
+ do_unicode_validation = true;
791
790
  break;
792
791
  case RailsXEsc: {
793
792
  long sz;
@@ -800,12 +799,22 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
800
799
  } else {
801
800
  size = (size_t)sz;
802
801
  }
802
+ do_unicode_validation = true;
803
803
  break;
804
804
  }
805
- case RailsEsc:
805
+ case RailsEsc: {
806
+ long sz;
806
807
  cmap = rails_friendly_chars;
807
- size = rails_friendly_size((uint8_t *)str, cnt);
808
+ sz = rails_friendly_size((uint8_t *)str, cnt);
809
+ if (sz < 0) {
810
+ has_hi = true;
811
+ size = (size_t)-sz;
812
+ } else {
813
+ size = (size_t)sz;
814
+ }
815
+ do_unicode_validation = true;
808
816
  break;
817
+ }
809
818
  case JSONEsc:
810
819
  default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
811
820
  }
@@ -813,10 +822,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
813
822
  *out->cur++ = '"';
814
823
 
815
824
  if (escape1) {
816
- *out->cur++ = '\\';
817
- *out->cur++ = 'u';
818
- *out->cur++ = '0';
819
- *out->cur++ = '0';
825
+ APPEND_CHARS(out->cur, "\\u00", 4);
820
826
  dump_hex((uint8_t)*str, out);
821
827
  cnt--;
822
828
  size--;
@@ -827,9 +833,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
827
833
  if (is_sym) {
828
834
  *out->cur++ = ':';
829
835
  }
830
- for (; '\0' != *str; str++) {
831
- *out->cur++ = *str;
832
- }
836
+ APPEND_CHARS(out->cur, str, cnt);
833
837
  *out->cur++ = '"';
834
838
  } else {
835
839
  const char *end = str + cnt;
@@ -841,8 +845,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
841
845
  for (; str < end; str++) {
842
846
  switch (cmap[(uint8_t)*str]) {
843
847
  case '1':
844
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
845
- check_start <= str) {
848
+ if (do_unicode_validation && check_start <= str) {
846
849
  if (0 != (0x80 & (uint8_t)*str)) {
847
850
  if (0xC0 == (0xC0 & (uint8_t)*str)) {
848
851
  check_start = check_unicode(str, end, orig);
@@ -866,11 +869,8 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
866
869
  }
867
870
  break;
868
871
  case '3': // Unicode
869
- if (0xe2 == (uint8_t)*str &&
870
- (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
871
- 2 <= end - str) {
872
- if (0x80 == (uint8_t)str[1] &&
873
- (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
872
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
873
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
874
874
  str = dump_unicode(str, end, out, orig);
875
875
  } else {
876
876
  check_start = check_unicode(str, end, orig);
@@ -882,17 +882,14 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
882
882
  break;
883
883
  case '6': // control characters
884
884
  if (*(uint8_t *)str < 0x80) {
885
- *out->cur++ = '\\';
886
- *out->cur++ = 'u';
887
- *out->cur++ = '0';
888
- *out->cur++ = '0';
885
+ if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
886
+ break;
887
+ }
888
+ APPEND_CHARS(out->cur, "\\u00", 4);
889
889
  dump_hex((uint8_t)*str, out);
890
890
  } else {
891
- if (0xe2 == (uint8_t)*str &&
892
- (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
893
- 2 <= end - str) {
894
- if (0x80 == (uint8_t)str[1] &&
895
- (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
891
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
892
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
896
893
  str = dump_unicode(str, end, out, orig);
897
894
  } else {
898
895
  check_start = check_unicode(str, end, orig);
@@ -908,8 +905,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
908
905
  }
909
906
  *out->cur++ = '"';
910
907
  }
911
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
912
- 0 < str - orig && 0 != (0x80 & *(str - 1))) {
908
+ if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
913
909
  uint8_t c = (uint8_t) * (str - 1);
914
910
  int i;
915
911
  int scnt = (int)(str - orig);
@@ -959,31 +955,43 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
959
955
  }
960
956
 
961
957
  void oj_dump_obj_to_s(VALUE obj, Out out) {
962
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
958
+ volatile VALUE rstr = oj_safe_string_convert(obj);
963
959
 
964
- oj_dump_cstr(rb_string_value_ptr((VALUE *)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
960
+ oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
965
961
  }
966
962
 
967
963
  void oj_dump_raw(const char *str, size_t cnt, Out out) {
968
964
  assure_size(out, cnt + 10);
969
- memcpy(out->cur, str, cnt);
970
- out->cur += cnt;
965
+ APPEND_CHARS(out->cur, str, cnt);
971
966
  *out->cur = '\0';
972
967
  }
973
968
 
969
+ void oj_out_init(Out out) {
970
+ out->buf = out->stack_buffer;
971
+ out->cur = out->buf;
972
+ out->end = out->buf + sizeof(out->stack_buffer) - BUFFER_EXTRA;
973
+ out->allocated = false;
974
+ }
975
+
976
+ void oj_out_free(Out out) {
977
+ if (out->allocated) {
978
+ OJ_R_FREE(out->buf); // TBD
979
+ }
980
+ }
981
+
974
982
  void oj_grow_out(Out out, size_t len) {
975
983
  size_t size = out->end - out->buf;
976
984
  long pos = out->cur - out->buf;
977
- char * buf = out->buf;
985
+ char *buf = out->buf;
978
986
 
979
987
  size *= 2;
980
988
  if (size <= len * 2 + pos) {
981
989
  size += len;
982
990
  }
983
991
  if (out->allocated) {
984
- REALLOC_N(buf, char, (size + BUFFER_EXTRA));
992
+ OJ_R_REALLOC_N(buf, char, (size + BUFFER_EXTRA));
985
993
  } else {
986
- buf = ALLOC_N(char, (size + BUFFER_EXTRA));
994
+ buf = OJ_R_ALLOC_N(char, (size + BUFFER_EXTRA));
987
995
  out->allocated = true;
988
996
  memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
989
997
  }
@@ -997,37 +1005,62 @@ void oj_grow_out(Out out, size_t len) {
997
1005
 
998
1006
  void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
999
1007
  assure_size(out, 4);
1000
- *out->cur++ = 'n';
1001
- *out->cur++ = 'u';
1002
- *out->cur++ = 'l';
1003
- *out->cur++ = 'l';
1004
- *out->cur = '\0';
1008
+ APPEND_CHARS(out->cur, "null", 4);
1009
+ *out->cur = '\0';
1005
1010
  }
1006
1011
 
1007
1012
  void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
1008
1013
  assure_size(out, 4);
1009
- *out->cur++ = 't';
1010
- *out->cur++ = 'r';
1011
- *out->cur++ = 'u';
1012
- *out->cur++ = 'e';
1013
- *out->cur = '\0';
1014
+ APPEND_CHARS(out->cur, "true", 4);
1015
+ *out->cur = '\0';
1014
1016
  }
1015
1017
 
1016
1018
  void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
1017
1019
  assure_size(out, 5);
1018
- *out->cur++ = 'f';
1019
- *out->cur++ = 'a';
1020
- *out->cur++ = 'l';
1021
- *out->cur++ = 's';
1022
- *out->cur++ = 'e';
1023
- *out->cur = '\0';
1020
+ APPEND_CHARS(out->cur, "false", 5);
1021
+ *out->cur = '\0';
1022
+ }
1023
+
1024
+ static const char digits_table[] = "\
1025
+ 00010203040506070809\
1026
+ 10111213141516171819\
1027
+ 20212223242526272829\
1028
+ 30313233343536373839\
1029
+ 40414243444546474849\
1030
+ 50515253545556575859\
1031
+ 60616263646566676869\
1032
+ 70717273747576777879\
1033
+ 80818283848586878889\
1034
+ 90919293949596979899";
1035
+
1036
+ char *oj_longlong_to_string(long long num, bool negative, char *buf) {
1037
+ while (100 <= num) {
1038
+ unsigned idx = num % 100 * 2;
1039
+ *buf-- = digits_table[idx + 1];
1040
+ *buf-- = digits_table[idx];
1041
+ num /= 100;
1042
+ }
1043
+ if (num < 10) {
1044
+ *buf-- = num + '0';
1045
+ } else {
1046
+ *buf-- = digits_table[num * 2 + 1];
1047
+ *buf-- = digits_table[num * 2];
1048
+ }
1049
+
1050
+ if (negative) {
1051
+ *buf = '-';
1052
+ } else {
1053
+ buf++;
1054
+ }
1055
+ return buf;
1024
1056
  }
1025
1057
 
1026
1058
  void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1027
1059
  char buf[32];
1028
- char * b = buf + sizeof(buf) - 1;
1029
- long long num = rb_num2ll(obj);
1030
- int neg = 0;
1060
+ char *b = buf + sizeof(buf) - 1;
1061
+ long long num = NUM2LL(obj);
1062
+ bool neg = false;
1063
+ size_t cnt = 0;
1031
1064
  bool dump_as_string = false;
1032
1065
 
1033
1066
  if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
@@ -1035,7 +1068,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1035
1068
  dump_as_string = true;
1036
1069
  }
1037
1070
  if (0 > num) {
1038
- neg = 1;
1071
+ neg = true;
1039
1072
  num = -num;
1040
1073
  }
1041
1074
  *b-- = '\0';
@@ -1044,24 +1077,16 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1044
1077
  *b-- = '"';
1045
1078
  }
1046
1079
  if (0 < num) {
1047
- for (; 0 < num; num /= 10, b--) {
1048
- *b = (num % 10) + '0';
1049
- }
1050
- if (neg) {
1051
- *b = '-';
1052
- } else {
1053
- b++;
1054
- }
1080
+ b = oj_longlong_to_string(num, neg, b);
1055
1081
  } else {
1056
1082
  *b = '0';
1057
1083
  }
1058
1084
  if (dump_as_string) {
1059
1085
  *--b = '"';
1060
1086
  }
1061
- assure_size(out, (sizeof(buf) - (b - buf)));
1062
- for (; '\0' != *b; b++) {
1063
- *out->cur++ = *b;
1064
- }
1087
+ cnt = sizeof(buf) - (b - buf) - 1;
1088
+ assure_size(out, cnt);
1089
+ APPEND_CHARS(out->cur, b, cnt);
1065
1090
  *out->cur = '\0';
1066
1091
  }
1067
1092
 
@@ -1070,16 +1095,14 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1070
1095
  int cnt = (int)RSTRING_LEN(rs);
1071
1096
  bool dump_as_string = false;
1072
1097
 
1073
- if (out->opts->int_range_max != 0 ||
1074
- out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1098
+ if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1075
1099
  dump_as_string = true;
1076
1100
  assure_size(out, cnt + 2);
1077
1101
  *out->cur++ = '"';
1078
1102
  } else {
1079
1103
  assure_size(out, cnt);
1080
1104
  }
1081
- memcpy(out->cur, rb_string_value_ptr((VALUE *)&rs), cnt);
1082
- out->cur += cnt;
1105
+ APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
1083
1106
  if (dump_as_string) {
1084
1107
  *out->cur++ = '"';
1085
1108
  }
@@ -1089,7 +1112,7 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1089
1112
  // Removed dependencies on math due to problems with CentOS 5.4.
1090
1113
  void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1091
1114
  char buf[64];
1092
- char * b;
1115
+ char *b;
1093
1116
  double d = rb_num2dbl(obj);
1094
1117
  int cnt = 0;
1095
1118
 
@@ -1168,7 +1191,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1168
1191
  } else if (isnan(d)) {
1169
1192
  if (ObjectMode == out->opts->mode) {
1170
1193
  strcpy(buf, nan_val);
1171
- cnt = sizeof(ninf_val) - 1;
1194
+ cnt = sizeof(nan_val) - 1;
1172
1195
  } else {
1173
1196
  NanDump nd = out->opts->dump_opts.nan_dump;
1174
1197
 
@@ -1200,21 +1223,19 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1200
1223
  } else if (d == (double)(long long int)d) {
1201
1224
  cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1202
1225
  } else if (0 == out->opts->float_prec) {
1203
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1226
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1204
1227
 
1205
1228
  cnt = (int)RSTRING_LEN(rstr);
1206
1229
  if ((int)sizeof(buf) <= cnt) {
1207
1230
  cnt = sizeof(buf) - 1;
1208
1231
  }
1209
- strncpy(buf, rb_string_value_ptr((VALUE *)&rstr), cnt);
1232
+ memcpy(buf, RSTRING_PTR(rstr), cnt);
1210
1233
  buf[cnt] = '\0';
1211
1234
  } else {
1212
1235
  cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
1213
1236
  }
1214
1237
  assure_size(out, cnt);
1215
- for (b = buf; '\0' != *b; b++) {
1216
- *out->cur++ = *b;
1217
- }
1238
+ APPEND_CHARS(out->cur, buf, cnt);
1218
1239
  *out->cur = '\0';
1219
1240
  }
1220
1241
 
@@ -1224,24 +1245,10 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
1224
1245
  // Round off issues at 16 significant digits so check for obvious ones of
1225
1246
  // 0001 and 9999.
1226
1247
  if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
1227
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1248
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1228
1249
 
1229
- strcpy(buf, rb_string_value_ptr((VALUE *)&rstr));
1250
+ strcpy(buf, RSTRING_PTR(rstr));
1230
1251
  cnt = (int)RSTRING_LEN(rstr);
1231
1252
  }
1232
1253
  return cnt;
1233
1254
  }
1234
-
1235
- bool oj_dump_ignore(Options opts, VALUE obj) {
1236
- if (NULL != opts->ignore && (ObjectMode == opts->mode || CustomMode == opts->mode)) {
1237
- VALUE *vp = opts->ignore;
1238
- VALUE clas = rb_obj_class(obj);
1239
-
1240
- for (; Qnil != *vp; vp++) {
1241
- if (clas == *vp) {
1242
- return true;
1243
- }
1244
- }
1245
- }
1246
- return false;
1247
- }