oj 3.13.9 → 3.16.1

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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -0
  3. data/README.md +13 -2
  4. data/ext/oj/buf.h +11 -6
  5. data/ext/oj/cache.c +25 -24
  6. data/ext/oj/cache8.c +10 -9
  7. data/ext/oj/circarray.c +8 -6
  8. data/ext/oj/circarray.h +2 -2
  9. data/ext/oj/code.c +19 -33
  10. data/ext/oj/code.h +2 -2
  11. data/ext/oj/compat.c +20 -60
  12. data/ext/oj/custom.c +76 -155
  13. data/ext/oj/debug.c +3 -9
  14. data/ext/oj/dump.c +203 -213
  15. data/ext/oj/dump.h +26 -12
  16. data/ext/oj/dump_compat.c +565 -642
  17. data/ext/oj/dump_leaf.c +17 -63
  18. data/ext/oj/dump_object.c +59 -181
  19. data/ext/oj/dump_strict.c +24 -48
  20. data/ext/oj/encoder.c +43 -0
  21. data/ext/oj/err.c +2 -13
  22. data/ext/oj/err.h +9 -12
  23. data/ext/oj/extconf.rb +18 -7
  24. data/ext/oj/fast.c +83 -108
  25. data/ext/oj/intern.c +52 -50
  26. data/ext/oj/intern.h +4 -8
  27. data/ext/oj/mem.c +318 -0
  28. data/ext/oj/mem.h +53 -0
  29. data/ext/oj/mimic_json.c +104 -81
  30. data/ext/oj/object.c +50 -67
  31. data/ext/oj/odd.c +89 -67
  32. data/ext/oj/odd.h +15 -15
  33. data/ext/oj/oj.c +171 -106
  34. data/ext/oj/oj.h +96 -74
  35. data/ext/oj/parse.c +169 -189
  36. data/ext/oj/parse.h +23 -24
  37. data/ext/oj/parser.c +89 -34
  38. data/ext/oj/parser.h +20 -9
  39. data/ext/oj/rails.c +86 -151
  40. data/ext/oj/rails.h +1 -1
  41. data/ext/oj/reader.c +12 -15
  42. data/ext/oj/reader.h +4 -2
  43. data/ext/oj/resolve.c +3 -4
  44. data/ext/oj/rxclass.c +6 -5
  45. data/ext/oj/rxclass.h +1 -1
  46. data/ext/oj/saj.c +21 -32
  47. data/ext/oj/saj2.c +329 -93
  48. data/ext/oj/saj2.h +23 -0
  49. data/ext/oj/scp.c +3 -14
  50. data/ext/oj/sparse.c +26 -70
  51. data/ext/oj/stream_writer.c +12 -22
  52. data/ext/oj/strict.c +20 -52
  53. data/ext/oj/string_writer.c +21 -22
  54. data/ext/oj/trace.h +31 -4
  55. data/ext/oj/usual.c +105 -150
  56. data/ext/oj/usual.h +68 -0
  57. data/ext/oj/util.h +1 -1
  58. data/ext/oj/val_stack.c +1 -1
  59. data/ext/oj/val_stack.h +8 -7
  60. data/ext/oj/validate.c +21 -26
  61. data/ext/oj/wab.c +32 -69
  62. data/lib/oj/active_support_helper.rb +1 -3
  63. data/lib/oj/bag.rb +7 -1
  64. data/lib/oj/easy_hash.rb +4 -5
  65. data/lib/oj/error.rb +0 -1
  66. data/lib/oj/json.rb +162 -150
  67. data/lib/oj/mimic.rb +6 -2
  68. data/lib/oj/saj.rb +20 -6
  69. data/lib/oj/state.rb +9 -6
  70. data/lib/oj/version.rb +1 -2
  71. data/lib/oj.rb +2 -0
  72. data/pages/Compatibility.md +1 -1
  73. data/pages/InstallOptions.md +20 -0
  74. data/pages/JsonGem.md +15 -0
  75. data/pages/Modes.md +6 -3
  76. data/pages/Options.md +10 -0
  77. data/pages/Rails.md +12 -0
  78. data/test/_test_active.rb +8 -9
  79. data/test/_test_active_mimic.rb +7 -8
  80. data/test/_test_mimic_rails.rb +17 -20
  81. data/test/activerecord/result_test.rb +5 -6
  82. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  83. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  84. data/test/{activesupport5 → activesupport7}/encoding_test.rb +20 -34
  85. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  86. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  87. data/test/files.rb +15 -15
  88. data/test/foo.rb +15 -15
  89. data/test/helper.rb +11 -8
  90. data/test/isolated/shared.rb +3 -2
  91. data/test/json_gem/json_addition_test.rb +2 -2
  92. data/test/json_gem/json_common_interface_test.rb +8 -6
  93. data/test/json_gem/json_encoding_test.rb +0 -0
  94. data/test/json_gem/json_ext_parser_test.rb +1 -0
  95. data/test/json_gem/json_fixtures_test.rb +3 -2
  96. data/test/json_gem/json_generator_test.rb +49 -37
  97. data/test/json_gem/json_generic_object_test.rb +11 -11
  98. data/test/json_gem/json_parser_test.rb +54 -47
  99. data/test/json_gem/json_string_matching_test.rb +9 -9
  100. data/test/json_gem/test_helper.rb +7 -3
  101. data/test/mem.rb +13 -12
  102. data/test/perf.rb +21 -26
  103. data/test/perf_compat.rb +31 -33
  104. data/test/perf_dump.rb +50 -0
  105. data/test/perf_fast.rb +80 -82
  106. data/test/perf_file.rb +27 -29
  107. data/test/perf_object.rb +65 -69
  108. data/test/perf_once.rb +12 -11
  109. data/test/perf_parser.rb +42 -48
  110. data/test/perf_saj.rb +46 -54
  111. data/test/perf_scp.rb +57 -69
  112. data/test/perf_simple.rb +41 -39
  113. data/test/perf_strict.rb +68 -70
  114. data/test/perf_wab.rb +67 -69
  115. data/test/prec.rb +3 -3
  116. data/test/sample/change.rb +0 -1
  117. data/test/sample/dir.rb +0 -1
  118. data/test/sample/doc.rb +0 -1
  119. data/test/sample/file.rb +0 -1
  120. data/test/sample/group.rb +0 -1
  121. data/test/sample/hasprops.rb +0 -1
  122. data/test/sample/layer.rb +0 -1
  123. data/test/sample/rect.rb +0 -1
  124. data/test/sample/shape.rb +0 -1
  125. data/test/sample/text.rb +0 -1
  126. data/test/sample.rb +16 -16
  127. data/test/sample_json.rb +8 -8
  128. data/test/test_compat.rb +95 -43
  129. data/test/test_custom.rb +72 -51
  130. data/test/test_debian.rb +7 -10
  131. data/test/test_fast.rb +102 -87
  132. data/test/test_file.rb +41 -30
  133. data/test/test_gc.rb +16 -5
  134. data/test/test_generate.rb +5 -5
  135. data/test/test_hash.rb +4 -4
  136. data/test/test_integer_range.rb +9 -9
  137. data/test/test_null.rb +20 -20
  138. data/test/test_object.rb +85 -96
  139. data/test/test_parser.rb +6 -22
  140. data/test/test_parser_debug.rb +27 -0
  141. data/test/test_parser_saj.rb +115 -23
  142. data/test/test_parser_usual.rb +6 -6
  143. data/test/test_rails.rb +2 -2
  144. data/test/test_saj.rb +10 -8
  145. data/test/test_scp.rb +37 -39
  146. data/test/test_strict.rb +40 -32
  147. data/test/test_various.rb +163 -84
  148. data/test/test_wab.rb +48 -44
  149. data/test/test_writer.rb +47 -47
  150. data/test/tests.rb +13 -5
  151. data/test/tests_mimic.rb +12 -3
  152. data/test/tests_mimic_addition.rb +12 -3
  153. metadata +34 -144
  154. data/test/activesupport4/decoding_test.rb +0 -108
  155. data/test/activesupport4/encoding_test.rb +0 -531
  156. data/test/activesupport4/test_helper.rb +0 -41
  157. data/test/activesupport5/test_helper.rb +0 -72
  158. data/test/bar.rb +0 -16
  159. data/test/baz.rb +0 -16
  160. data/test/bug.rb +0 -16
  161. 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,7 @@ 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;
193
-
194
- for (; 0 < i; str++, i--) {
195
- size += rails_friendly_chars[*str];
196
- }
197
- return size - len * (size_t)'0';
201
+ return calculate_string_size(str, len, rails_friendly_chars);
198
202
  }
199
203
 
200
204
  const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
@@ -247,7 +251,7 @@ inline static void dump_hex(uint8_t c, Out out) {
247
251
  static void raise_invalid_unicode(const char *str, int len, int pos) {
248
252
  char c;
249
253
  char code[32];
250
- char * cp = code;
254
+ char *cp = code;
251
255
  int i;
252
256
  uint8_t d;
253
257
 
@@ -302,16 +306,14 @@ static const char *dump_unicode(const char *str, const char *end, Out out, const
302
306
  uint32_t c1;
303
307
 
304
308
  code -= 0x00010000;
305
- c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
306
- code = (code & 0x000003FF) + 0x0000DC00;
307
- *out->cur++ = '\\';
308
- *out->cur++ = 'u';
309
+ c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
310
+ code = (code & 0x000003FF) + 0x0000DC00;
311
+ APPEND_CHARS(out->cur, "\\u", 2);
309
312
  for (i = 3; 0 <= i; i--) {
310
313
  *out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
311
314
  }
312
315
  }
313
- *out->cur++ = '\\';
314
- *out->cur++ = 'u';
316
+ APPEND_CHARS(out->cur, "\\u", 2);
315
317
  for (i = 3; 0 <= i; i--) {
316
318
  *out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
317
319
  }
@@ -360,9 +362,7 @@ long oj_check_circular(VALUE obj, Out out) {
360
362
  } else {
361
363
  if (ObjectMode == out->opts->mode) {
362
364
  assure_size(out, 18);
363
- *out->cur++ = '"';
364
- *out->cur++ = '^';
365
- *out->cur++ = 'r';
365
+ APPEND_CHARS(out->cur, "\"^r", 3);
366
366
  dump_ulong(id, out);
367
367
  *out->cur++ = '"';
368
368
  }
@@ -374,15 +374,14 @@ long oj_check_circular(VALUE obj, Out out) {
374
374
 
375
375
  void oj_dump_time(VALUE obj, Out out, int withZone) {
376
376
  char buf[64];
377
- char * b = buf + sizeof(buf) - 1;
377
+ char *b = buf + sizeof(buf) - 1;
378
378
  long size;
379
- char * dot;
379
+ char *dot;
380
380
  int neg = 0;
381
381
  long one = 1000000000;
382
382
  long long sec;
383
383
  long long nsec;
384
384
 
385
- #ifdef HAVE_RB_TIME_TIMESPEC
386
385
  // rb_time_timespec as well as rb_time_timeeval have a bug that causes an
387
386
  // exception to be raised if a time is before 1970 on 32 bit systems so
388
387
  // check the timespec size and use the ruby calls if a 32 bit system.
@@ -392,13 +391,9 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
392
391
  sec = (long long)ts.tv_sec;
393
392
  nsec = ts.tv_nsec;
394
393
  } 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));
394
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
395
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
397
396
  }
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
397
 
403
398
  *b-- = '\0';
404
399
  if (withZone) {
@@ -464,13 +459,12 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
464
459
  b++;
465
460
  size = sizeof(buf) - (b - buf) - 1;
466
461
  assure_size(out, size);
467
- memcpy(out->cur, b, size);
468
- out->cur += size;
462
+ APPEND_CHARS(out->cur, b, size);
469
463
  *out->cur = '\0';
470
464
  }
471
465
 
472
466
  void oj_dump_ruby_time(VALUE obj, Out out) {
473
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
467
+ volatile VALUE rstr = oj_safe_string_convert(obj);
474
468
 
475
469
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
476
470
  }
@@ -485,20 +479,15 @@ void oj_dump_xml_time(VALUE obj, Out out) {
485
479
  int tzhour, tzmin;
486
480
  char tzsign = '+';
487
481
 
488
- #ifdef HAVE_RB_TIME_TIMESPEC
489
482
  if (16 <= sizeof(struct timespec)) {
490
483
  struct timespec ts = rb_time_timespec(obj);
491
484
 
492
485
  sec = ts.tv_sec;
493
486
  nsec = ts.tv_nsec;
494
487
  } 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));
488
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
489
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
497
490
  }
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
491
 
503
492
  assure_size(out, 36);
504
493
  if (9 > out->opts->sec_prec) {
@@ -535,14 +524,7 @@ void oj_dump_xml_time(VALUE obj, Out out) {
535
524
  }
536
525
  if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
537
526
  if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
538
- int len = 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);
527
+ int len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
546
528
  oj_dump_cstr(buf, len, 0, 0, out);
547
529
  } else {
548
530
  int len = sprintf(buf,
@@ -574,18 +556,7 @@ void oj_dump_xml_time(VALUE obj, Out out) {
574
556
  if (9 > out->opts->sec_prec) {
575
557
  format[32] = '0' + out->opts->sec_prec;
576
558
  }
577
- len = sprintf(buf,
578
- format,
579
- ti.year,
580
- ti.mon,
581
- ti.day,
582
- ti.hour,
583
- ti.min,
584
- ti.sec,
585
- (long)nsec,
586
- tzsign,
587
- tzhour,
588
- tzmin);
559
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
589
560
  oj_dump_cstr(buf, len, 0, 0, out);
590
561
  }
591
562
  }
@@ -596,12 +567,8 @@ void oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
596
567
 
597
568
  void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
598
569
  if (0 == out->buf) {
599
- out->buf = ALLOC_N(char, 4096);
600
- // 1 less than end plus extra for possible errors
601
- out->end = out->buf + 4095 - BUFFER_EXTRA;
602
- out->allocated = true;
570
+ oj_out_init(out);
603
571
  }
604
- out->cur = out->buf;
605
572
  out->circ_cnt = 0;
606
573
  out->opts = copts;
607
574
  out->hash_cnt = 0;
@@ -636,38 +603,51 @@ void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int arg
636
603
  }
637
604
 
638
605
  void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
639
- char buf[4096];
640
606
  struct _out out;
641
607
  size_t size;
642
- FILE * f;
608
+ FILE *f;
643
609
  int ok;
644
610
 
645
- out.buf = buf;
646
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
647
- out.allocated = false;
648
- out.omit_nil = copts->dump_opts.omit_nil;
611
+ oj_out_init(&out);
612
+
613
+ out.omit_nil = copts->dump_opts.omit_nil;
649
614
  oj_dump_obj_to_json(obj, copts, &out);
650
615
  size = out.cur - out.buf;
651
616
  if (0 == (f = fopen(path, "w"))) {
652
- if (out.allocated) {
653
- xfree(out.buf);
654
- }
617
+ oj_out_free(&out);
655
618
  rb_raise(rb_eIOError, "%s", strerror(errno));
656
619
  }
657
620
  ok = (size == fwrite(out.buf, 1, size, f));
658
- if (out.allocated) {
659
- xfree(out.buf);
660
- }
661
- fclose(f);
621
+
622
+ oj_out_free(&out);
623
+
662
624
  if (!ok) {
663
625
  int err = ferror(f);
626
+ fclose(f);
664
627
 
665
628
  rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
666
629
  }
630
+ fclose(f);
631
+ }
632
+
633
+ #if !IS_WINDOWS
634
+ static void write_ready(int fd) {
635
+ struct pollfd pp;
636
+ int i;
637
+
638
+ pp.fd = fd;
639
+ pp.events = POLLERR | POLLOUT;
640
+ pp.revents = 0;
641
+ if (0 >= (i = poll(&pp, 1, 5000))) {
642
+ if (0 == i || EAGAIN == errno) {
643
+ rb_raise(rb_eIOError, "write timed out");
644
+ }
645
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
646
+ }
667
647
  }
648
+ #endif
668
649
 
669
650
  void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
670
- char buf[4096];
671
651
  struct _out out;
672
652
  ssize_t size;
673
653
  VALUE clas = rb_obj_class(stream);
@@ -676,42 +656,49 @@ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
676
656
  VALUE s;
677
657
  #endif
678
658
 
679
- out.buf = buf;
680
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
681
- out.allocated = false;
682
- out.omit_nil = copts->dump_opts.omit_nil;
659
+ oj_out_init(&out);
660
+
661
+ out.omit_nil = copts->dump_opts.omit_nil;
683
662
  oj_dump_obj_to_json(obj, copts, &out);
684
663
  size = out.cur - out.buf;
685
664
  if (oj_stringio_class == clas) {
686
665
  rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
687
666
  #if !IS_WINDOWS
688
- } else if (rb_respond_to(stream, oj_fileno_id) &&
689
- Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) && 0 != (fd = FIX2INT(s))) {
690
- if (size != write(fd, out.buf, size)) {
691
- if (out.allocated) {
692
- xfree(out.buf);
667
+ } else if (rb_respond_to(stream, oj_fileno_id) && Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
668
+ 0 != (fd = FIX2INT(s))) {
669
+ ssize_t cnt;
670
+ ssize_t total = 0;
671
+
672
+ while (true) {
673
+ if (0 > (cnt = write(fd, out.buf + total, size - total))) {
674
+ if (EAGAIN != errno) {
675
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
676
+ break;
677
+ }
693
678
  }
694
- rb_raise(rb_eIOError, "Write failed. [%d:%s]", errno, strerror(errno));
679
+ total += cnt;
680
+ if (size <= total) {
681
+ // Completed
682
+ break;
683
+ }
684
+ write_ready(fd);
695
685
  }
696
686
  #endif
697
687
  } else if (rb_respond_to(stream, oj_write_id)) {
698
688
  rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
699
689
  } else {
700
- if (out.allocated) {
701
- xfree(out.buf);
702
- }
690
+ oj_out_free(&out);
703
691
  rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
704
692
  }
705
- if (out.allocated) {
706
- xfree(out.buf);
707
- }
693
+ oj_out_free(&out);
708
694
  }
709
695
 
710
696
  void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
711
- rb_encoding *enc = rb_enc_get(obj);
697
+ int idx = RB_ENCODING_GET(obj);
712
698
 
713
- if (oj_utf8_encoding != enc) {
714
- obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
699
+ if (oj_utf8_encoding_index != idx) {
700
+ rb_encoding *enc = rb_enc_from_index(idx);
701
+ obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
715
702
  }
716
703
  oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
717
704
  }
@@ -724,7 +711,7 @@ void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
724
711
 
725
712
  static void debug_raise(const char *orig, size_t cnt, int line) {
726
713
  char buf[1024];
727
- char * b = buf;
714
+ char *b = buf;
728
715
  const char *s = orig;
729
716
  const char *s_end = s + cnt;
730
717
 
@@ -750,20 +737,16 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
750
737
  } else {
751
738
  volatile VALUE jv;
752
739
 
753
- if (Yes == out->opts->trace) {
754
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
755
- }
740
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyIn);
756
741
  jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
757
- if (Yes == out->opts->trace) {
758
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
759
- }
742
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyOut);
760
743
  oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
761
744
  }
762
745
  }
763
746
 
764
747
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
765
748
  size_t size;
766
- char * cmap;
749
+ char *cmap;
767
750
  const char *orig = str;
768
751
  bool has_hi = false;
769
752
 
@@ -776,6 +759,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
776
759
  cmap = ascii_friendly_chars;
777
760
  size = ascii_friendly_size((uint8_t *)str, cnt);
778
761
  break;
762
+ case SlashEsc:
763
+ has_hi = true;
764
+ cmap = slash_friendly_chars;
765
+ size = slash_friendly_size((uint8_t *)str, cnt);
766
+ break;
779
767
  case XSSEsc:
780
768
  cmap = xss_friendly_chars;
781
769
  size = xss_friendly_size((uint8_t *)str, cnt);
@@ -808,10 +796,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
808
796
  *out->cur++ = '"';
809
797
 
810
798
  if (escape1) {
811
- *out->cur++ = '\\';
812
- *out->cur++ = 'u';
813
- *out->cur++ = '0';
814
- *out->cur++ = '0';
799
+ APPEND_CHARS(out->cur, "\\u00", 4);
815
800
  dump_hex((uint8_t)*str, out);
816
801
  cnt--;
817
802
  size--;
@@ -822,8 +807,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
822
807
  if (is_sym) {
823
808
  *out->cur++ = ':';
824
809
  }
825
- memcpy(out->cur, str, cnt);
826
- out->cur += cnt;
810
+ APPEND_CHARS(out->cur, str, cnt);
827
811
  *out->cur++ = '"';
828
812
  } else {
829
813
  const char *end = str + cnt;
@@ -835,8 +819,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
835
819
  for (; str < end; str++) {
836
820
  switch (cmap[(uint8_t)*str]) {
837
821
  case '1':
838
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
839
- check_start <= str) {
822
+ if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && check_start <= str) {
840
823
  if (0 != (0x80 & (uint8_t)*str)) {
841
824
  if (0xC0 == (0xC0 & (uint8_t)*str)) {
842
825
  check_start = check_unicode(str, end, orig);
@@ -860,11 +843,9 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
860
843
  }
861
844
  break;
862
845
  case '3': // Unicode
863
- if (0xe2 == (uint8_t)*str &&
864
- (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
846
+ if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
865
847
  2 <= end - str) {
866
- if (0x80 == (uint8_t)str[1] &&
867
- (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
848
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
868
849
  str = dump_unicode(str, end, out, orig);
869
850
  } else {
870
851
  check_start = check_unicode(str, end, orig);
@@ -876,17 +857,15 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
876
857
  break;
877
858
  case '6': // control characters
878
859
  if (*(uint8_t *)str < 0x80) {
879
- *out->cur++ = '\\';
880
- *out->cur++ = 'u';
881
- *out->cur++ = '0';
882
- *out->cur++ = '0';
860
+ if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
861
+ break;
862
+ }
863
+ APPEND_CHARS(out->cur, "\\u00", 4);
883
864
  dump_hex((uint8_t)*str, out);
884
865
  } else {
885
866
  if (0xe2 == (uint8_t)*str &&
886
- (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
887
- 2 <= end - str) {
888
- if (0x80 == (uint8_t)str[1] &&
889
- (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
867
+ (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
868
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
890
869
  str = dump_unicode(str, end, out, orig);
891
870
  } else {
892
871
  check_start = check_unicode(str, end, orig);
@@ -902,8 +881,8 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
902
881
  }
903
882
  *out->cur++ = '"';
904
883
  }
905
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
906
- 0 < str - orig && 0 != (0x80 & *(str - 1))) {
884
+ if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 0 < str - orig &&
885
+ 0 != (0x80 & *(str - 1))) {
907
886
  uint8_t c = (uint8_t) * (str - 1);
908
887
  int i;
909
888
  int scnt = (int)(str - orig);
@@ -953,31 +932,43 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
953
932
  }
954
933
 
955
934
  void oj_dump_obj_to_s(VALUE obj, Out out) {
956
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
935
+ volatile VALUE rstr = oj_safe_string_convert(obj);
957
936
 
958
937
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
959
938
  }
960
939
 
961
940
  void oj_dump_raw(const char *str, size_t cnt, Out out) {
962
941
  assure_size(out, cnt + 10);
963
- memcpy(out->cur, str, cnt);
964
- out->cur += cnt;
942
+ APPEND_CHARS(out->cur, str, cnt);
965
943
  *out->cur = '\0';
966
944
  }
967
945
 
946
+ void oj_out_init(Out out) {
947
+ out->buf = out->stack_buffer;
948
+ out->cur = out->buf;
949
+ out->end = out->buf + sizeof(out->stack_buffer) - BUFFER_EXTRA;
950
+ out->allocated = false;
951
+ }
952
+
953
+ void oj_out_free(Out out) {
954
+ if (out->allocated) {
955
+ OJ_R_FREE(out->buf); // TBD
956
+ }
957
+ }
958
+
968
959
  void oj_grow_out(Out out, size_t len) {
969
960
  size_t size = out->end - out->buf;
970
961
  long pos = out->cur - out->buf;
971
- char * buf = out->buf;
962
+ char *buf = out->buf;
972
963
 
973
964
  size *= 2;
974
965
  if (size <= len * 2 + pos) {
975
966
  size += len;
976
967
  }
977
968
  if (out->allocated) {
978
- REALLOC_N(buf, char, (size + BUFFER_EXTRA));
969
+ OJ_R_REALLOC_N(buf, char, (size + BUFFER_EXTRA));
979
970
  } else {
980
- buf = ALLOC_N(char, (size + BUFFER_EXTRA));
971
+ buf = OJ_R_ALLOC_N(char, (size + BUFFER_EXTRA));
981
972
  out->allocated = true;
982
973
  memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
983
974
  }
@@ -991,37 +982,62 @@ void oj_grow_out(Out out, size_t len) {
991
982
 
992
983
  void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
993
984
  assure_size(out, 4);
994
- *out->cur++ = 'n';
995
- *out->cur++ = 'u';
996
- *out->cur++ = 'l';
997
- *out->cur++ = 'l';
998
- *out->cur = '\0';
985
+ APPEND_CHARS(out->cur, "null", 4);
986
+ *out->cur = '\0';
999
987
  }
1000
988
 
1001
989
  void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
1002
990
  assure_size(out, 4);
1003
- *out->cur++ = 't';
1004
- *out->cur++ = 'r';
1005
- *out->cur++ = 'u';
1006
- *out->cur++ = 'e';
1007
- *out->cur = '\0';
991
+ APPEND_CHARS(out->cur, "true", 4);
992
+ *out->cur = '\0';
1008
993
  }
1009
994
 
1010
995
  void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
1011
996
  assure_size(out, 5);
1012
- *out->cur++ = 'f';
1013
- *out->cur++ = 'a';
1014
- *out->cur++ = 'l';
1015
- *out->cur++ = 's';
1016
- *out->cur++ = 'e';
1017
- *out->cur = '\0';
997
+ APPEND_CHARS(out->cur, "false", 5);
998
+ *out->cur = '\0';
999
+ }
1000
+
1001
+ static const char digits_table[] = "\
1002
+ 00010203040506070809\
1003
+ 10111213141516171819\
1004
+ 20212223242526272829\
1005
+ 30313233343536373839\
1006
+ 40414243444546474849\
1007
+ 50515253545556575859\
1008
+ 60616263646566676869\
1009
+ 70717273747576777879\
1010
+ 80818283848586878889\
1011
+ 90919293949596979899";
1012
+
1013
+ char *oj_longlong_to_string(long long num, bool negative, char *buf) {
1014
+ while (100 <= num) {
1015
+ unsigned idx = num % 100 * 2;
1016
+ *buf-- = digits_table[idx + 1];
1017
+ *buf-- = digits_table[idx];
1018
+ num /= 100;
1019
+ }
1020
+ if (num < 10) {
1021
+ *buf-- = num + '0';
1022
+ } else {
1023
+ *buf-- = digits_table[num * 2 + 1];
1024
+ *buf-- = digits_table[num * 2];
1025
+ }
1026
+
1027
+ if (negative) {
1028
+ *buf = '-';
1029
+ } else {
1030
+ buf++;
1031
+ }
1032
+ return buf;
1018
1033
  }
1019
1034
 
1020
1035
  void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1021
1036
  char buf[32];
1022
- char * b = buf + sizeof(buf) - 1;
1023
- long long num = rb_num2ll(obj);
1024
- int neg = 0;
1037
+ char *b = buf + sizeof(buf) - 1;
1038
+ long long num = NUM2LL(obj);
1039
+ bool neg = false;
1040
+ size_t cnt = 0;
1025
1041
  bool dump_as_string = false;
1026
1042
 
1027
1043
  if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
@@ -1029,7 +1045,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1029
1045
  dump_as_string = true;
1030
1046
  }
1031
1047
  if (0 > num) {
1032
- neg = 1;
1048
+ neg = true;
1033
1049
  num = -num;
1034
1050
  }
1035
1051
  *b-- = '\0';
@@ -1038,24 +1054,16 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1038
1054
  *b-- = '"';
1039
1055
  }
1040
1056
  if (0 < num) {
1041
- for (; 0 < num; num /= 10, b--) {
1042
- *b = (num % 10) + '0';
1043
- }
1044
- if (neg) {
1045
- *b = '-';
1046
- } else {
1047
- b++;
1048
- }
1057
+ b = oj_longlong_to_string(num, neg, b);
1049
1058
  } else {
1050
1059
  *b = '0';
1051
1060
  }
1052
1061
  if (dump_as_string) {
1053
1062
  *--b = '"';
1054
1063
  }
1055
- assure_size(out, (sizeof(buf) - (b - buf)));
1056
- for (; '\0' != *b; b++) {
1057
- *out->cur++ = *b;
1058
- }
1064
+ cnt = sizeof(buf) - (b - buf) - 1;
1065
+ assure_size(out, cnt);
1066
+ APPEND_CHARS(out->cur, b, cnt);
1059
1067
  *out->cur = '\0';
1060
1068
  }
1061
1069
 
@@ -1064,16 +1072,14 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1064
1072
  int cnt = (int)RSTRING_LEN(rs);
1065
1073
  bool dump_as_string = false;
1066
1074
 
1067
- if (out->opts->int_range_max != 0 ||
1068
- out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1075
+ if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1069
1076
  dump_as_string = true;
1070
1077
  assure_size(out, cnt + 2);
1071
1078
  *out->cur++ = '"';
1072
1079
  } else {
1073
1080
  assure_size(out, cnt);
1074
1081
  }
1075
- memcpy(out->cur, RSTRING_PTR(rs), cnt);
1076
- out->cur += cnt;
1082
+ APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
1077
1083
  if (dump_as_string) {
1078
1084
  *out->cur++ = '"';
1079
1085
  }
@@ -1083,7 +1089,7 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1083
1089
  // Removed dependencies on math due to problems with CentOS 5.4.
1084
1090
  void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1085
1091
  char buf[64];
1086
- char * b;
1092
+ char *b;
1087
1093
  double d = rb_num2dbl(obj);
1088
1094
  int cnt = 0;
1089
1095
 
@@ -1162,7 +1168,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1162
1168
  } else if (isnan(d)) {
1163
1169
  if (ObjectMode == out->opts->mode) {
1164
1170
  strcpy(buf, nan_val);
1165
- cnt = sizeof(ninf_val) - 1;
1171
+ cnt = sizeof(nan_val) - 1;
1166
1172
  } else {
1167
1173
  NanDump nd = out->opts->dump_opts.nan_dump;
1168
1174
 
@@ -1194,7 +1200,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1194
1200
  } else if (d == (double)(long long int)d) {
1195
1201
  cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1196
1202
  } else if (0 == out->opts->float_prec) {
1197
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1203
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1198
1204
 
1199
1205
  cnt = (int)RSTRING_LEN(rstr);
1200
1206
  if ((int)sizeof(buf) <= cnt) {
@@ -1206,9 +1212,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1206
1212
  cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
1207
1213
  }
1208
1214
  assure_size(out, cnt);
1209
- for (b = buf; '\0' != *b; b++) {
1210
- *out->cur++ = *b;
1211
- }
1215
+ APPEND_CHARS(out->cur, buf, cnt);
1212
1216
  *out->cur = '\0';
1213
1217
  }
1214
1218
 
@@ -1218,24 +1222,10 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
1218
1222
  // Round off issues at 16 significant digits so check for obvious ones of
1219
1223
  // 0001 and 9999.
1220
1224
  if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
1221
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1225
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1222
1226
 
1223
1227
  strcpy(buf, RSTRING_PTR(rstr));
1224
1228
  cnt = (int)RSTRING_LEN(rstr);
1225
1229
  }
1226
1230
  return cnt;
1227
1231
  }
1228
-
1229
- bool oj_dump_ignore(Options opts, VALUE obj) {
1230
- if (NULL != opts->ignore && (ObjectMode == opts->mode || CustomMode == opts->mode)) {
1231
- VALUE *vp = opts->ignore;
1232
- VALUE clas = rb_obj_class(obj);
1233
-
1234
- for (; Qnil != *vp; vp++) {
1235
- if (clas == *vp) {
1236
- return true;
1237
- }
1238
- }
1239
- }
1240
- return false;
1241
- }