oj 3.13.9 → 3.16.1

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