oj 3.11.5 → 3.16.5

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