oj 3.13.7 → 3.13.23

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +75 -0
  3. data/README.md +11 -0
  4. data/ext/oj/buf.h +4 -0
  5. data/ext/oj/circarray.c +1 -1
  6. data/ext/oj/code.c +15 -22
  7. data/ext/oj/compat.c +10 -10
  8. data/ext/oj/custom.c +66 -112
  9. data/ext/oj/dump.c +147 -184
  10. data/ext/oj/dump.h +25 -8
  11. data/ext/oj/dump_compat.c +47 -89
  12. data/ext/oj/dump_leaf.c +14 -58
  13. data/ext/oj/dump_object.c +72 -188
  14. data/ext/oj/dump_strict.c +19 -31
  15. data/ext/oj/encoder.c +43 -0
  16. data/ext/oj/extconf.rb +5 -4
  17. data/ext/oj/fast.c +36 -24
  18. data/ext/oj/intern.c +22 -12
  19. data/ext/oj/intern.h +1 -1
  20. data/ext/oj/mimic_json.c +74 -73
  21. data/ext/oj/object.c +54 -72
  22. data/ext/oj/odd.c +83 -63
  23. data/ext/oj/odd.h +13 -13
  24. data/ext/oj/oj.c +166 -175
  25. data/ext/oj/oj.h +25 -3
  26. data/ext/oj/parse.c +123 -79
  27. data/ext/oj/parse.h +2 -0
  28. data/ext/oj/parser.c +77 -21
  29. data/ext/oj/parser.h +12 -0
  30. data/ext/oj/rails.c +46 -70
  31. data/ext/oj/rails.h +1 -1
  32. data/ext/oj/reader.c +2 -0
  33. data/ext/oj/saj.c +11 -23
  34. data/ext/oj/saj2.c +333 -85
  35. data/ext/oj/saj2.h +23 -0
  36. data/ext/oj/sparse.c +4 -0
  37. data/ext/oj/stream_writer.c +3 -1
  38. data/ext/oj/strict.c +13 -13
  39. data/ext/oj/string_writer.c +12 -5
  40. data/ext/oj/usual.c +86 -131
  41. data/ext/oj/usual.h +68 -0
  42. data/ext/oj/val_stack.c +1 -1
  43. data/ext/oj/validate.c +21 -26
  44. data/ext/oj/wab.c +22 -27
  45. data/lib/oj/saj.rb +20 -6
  46. data/lib/oj/state.rb +1 -1
  47. data/lib/oj/version.rb +1 -1
  48. data/pages/Compatibility.md +1 -1
  49. data/pages/JsonGem.md +15 -0
  50. data/pages/Modes.md +6 -3
  51. data/pages/Options.md +6 -0
  52. data/pages/Rails.md +12 -0
  53. data/test/activesupport7/abstract_unit.rb +49 -0
  54. data/test/activesupport7/decoding_test.rb +125 -0
  55. data/test/activesupport7/encoding_test.rb +486 -0
  56. data/test/activesupport7/encoding_test_cases.rb +104 -0
  57. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  58. data/test/bar.rb +3 -8
  59. data/test/bug.rb +16 -0
  60. data/test/foo.rb +71 -7
  61. data/test/helper.rb +8 -2
  62. data/test/json_gem/json_generator_test.rb +5 -4
  63. data/test/json_gem/json_parser_test.rb +8 -1
  64. data/test/json_gem/test_helper.rb +7 -3
  65. data/test/perf_dump.rb +50 -0
  66. data/test/test_compat.rb +25 -0
  67. data/test/test_custom.rb +13 -2
  68. data/test/test_fast.rb +37 -7
  69. data/test/test_file.rb +23 -7
  70. data/test/test_gc.rb +11 -0
  71. data/test/test_object.rb +8 -10
  72. data/test/test_parser.rb +3 -19
  73. data/test/test_parser_debug.rb +27 -0
  74. data/test/test_parser_saj.rb +92 -2
  75. data/test/test_saj.rb +1 -1
  76. data/test/test_scp.rb +2 -4
  77. data/test/test_strict.rb +2 -0
  78. data/test/test_various.rb +32 -2
  79. data/test/test_wab.rb +2 -0
  80. data/test/tests.rb +9 -1
  81. data/test/tests_mimic.rb +9 -0
  82. data/test/tests_mimic_addition.rb +9 -0
  83. metadata +15 -115
data/ext/oj/dump.c CHANGED
@@ -10,6 +10,9 @@
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"
15
18
  #include "odd.h"
@@ -29,6 +32,7 @@ static const char nan_val[] = NAN_VAL;
29
32
  typedef unsigned long ulong;
30
33
 
31
34
  static size_t hibit_friendly_size(const uint8_t *str, size_t len);
35
+ static size_t slash_friendly_size(const uint8_t *str, size_t len);
32
36
  static size_t xss_friendly_size(const uint8_t *str, size_t len);
33
37
  static size_t ascii_friendly_size(const uint8_t *str, size_t len);
34
38
 
@@ -56,6 +60,17 @@ static char hibit_friendly_chars[256] = "\
56
60
  11111111111111111111111111111111\
57
61
  11111111111111111111111111111111";
58
62
 
63
+ // JSON standard but escape forward slashes `/`
64
+ static char slash_friendly_chars[256] = "\
65
+ 66666666222622666666666666666666\
66
+ 11211111111111121111111111111111\
67
+ 11111111111111111111111111112111\
68
+ 11111111111111111111111111111111\
69
+ 11111111111111111111111111111111\
70
+ 11111111111111111111111111111111\
71
+ 11111111111111111111111111111111\
72
+ 11111111111111111111111111111111";
73
+
59
74
  // High bit set characters are always encoded as unicode. Worse case is 3
60
75
  // bytes per character in the output. That makes this conservative.
61
76
  static char ascii_friendly_chars[256] = "\
@@ -113,49 +128,43 @@ static char rails_friendly_chars[256] = "\
113
128
  11111111111111111111111111111111";
114
129
 
115
130
  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)));
131
+ rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.", rb_class2name(rb_obj_class(obj)));
119
132
  }
120
133
 
121
- inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
134
+ inline static size_t calculate_string_size(const uint8_t *str, size_t len, const char *table) {
122
135
  size_t size = 0;
123
136
  size_t i = len;
124
137
 
125
- for (; 0 < i; str++, i--) {
126
- size += newline_friendly_chars[*str];
138
+ for (; 3 < i; i -= 4) {
139
+ size += table[*str++];
140
+ size += table[*str++];
141
+ size += table[*str++];
142
+ size += table[*str++];
143
+ }
144
+ for (; 0 < i; i--) {
145
+ size += table[*str++];
127
146
  }
128
147
  return size - len * (size_t)'0';
129
148
  }
130
149
 
150
+ inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
151
+ return calculate_string_size(str, len, newline_friendly_chars);
152
+ }
153
+
131
154
  inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
132
- size_t size = 0;
133
- size_t i = len;
155
+ return calculate_string_size(str, len, hibit_friendly_chars);
156
+ }
134
157
 
135
- for (; 0 < i; str++, i--) {
136
- size += hibit_friendly_chars[*str];
137
- }
138
- return size - len * (size_t)'0';
158
+ inline static size_t slash_friendly_size(const uint8_t *str, size_t len) {
159
+ return calculate_string_size(str, len, slash_friendly_chars);
139
160
  }
140
161
 
141
162
  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';
163
+ return calculate_string_size(str, len, ascii_friendly_chars);
149
164
  }
150
165
 
151
166
  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';
167
+ return calculate_string_size(str, len, xss_friendly_chars);
159
168
  }
160
169
 
161
170
  inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
@@ -188,13 +197,7 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
188
197
  }
189
198
 
190
199
  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';
200
+ return calculate_string_size(str, len, rails_friendly_chars);
198
201
  }
199
202
 
200
203
  const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
@@ -247,7 +250,7 @@ inline static void dump_hex(uint8_t c, Out out) {
247
250
  static void raise_invalid_unicode(const char *str, int len, int pos) {
248
251
  char c;
249
252
  char code[32];
250
- char * cp = code;
253
+ char *cp = code;
251
254
  int i;
252
255
  uint8_t d;
253
256
 
@@ -304,14 +307,12 @@ static const char *dump_unicode(const char *str, const char *end, Out out, const
304
307
  code -= 0x00010000;
305
308
  c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
306
309
  code = (code & 0x000003FF) + 0x0000DC00;
307
- *out->cur++ = '\\';
308
- *out->cur++ = 'u';
310
+ APPEND_CHARS(out->cur, "\\u", 2);
309
311
  for (i = 3; 0 <= i; i--) {
310
312
  *out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
311
313
  }
312
314
  }
313
- *out->cur++ = '\\';
314
- *out->cur++ = 'u';
315
+ APPEND_CHARS(out->cur, "\\u", 2);
315
316
  for (i = 3; 0 <= i; i--) {
316
317
  *out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
317
318
  }
@@ -360,9 +361,7 @@ long oj_check_circular(VALUE obj, Out out) {
360
361
  } else {
361
362
  if (ObjectMode == out->opts->mode) {
362
363
  assure_size(out, 18);
363
- *out->cur++ = '"';
364
- *out->cur++ = '^';
365
- *out->cur++ = 'r';
364
+ APPEND_CHARS(out->cur, "\"^r", 3);
366
365
  dump_ulong(id, out);
367
366
  *out->cur++ = '"';
368
367
  }
@@ -374,15 +373,14 @@ long oj_check_circular(VALUE obj, Out out) {
374
373
 
375
374
  void oj_dump_time(VALUE obj, Out out, int withZone) {
376
375
  char buf[64];
377
- char * b = buf + sizeof(buf) - 1;
376
+ char *b = buf + sizeof(buf) - 1;
378
377
  long size;
379
- char * dot;
378
+ char *dot;
380
379
  int neg = 0;
381
380
  long one = 1000000000;
382
381
  long long sec;
383
382
  long long nsec;
384
383
 
385
- #ifdef HAVE_RB_TIME_TIMESPEC
386
384
  // rb_time_timespec as well as rb_time_timeeval have a bug that causes an
387
385
  // exception to be raised if a time is before 1970 on 32 bit systems so
388
386
  // check the timespec size and use the ruby calls if a 32 bit system.
@@ -392,13 +390,9 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
392
390
  sec = (long long)ts.tv_sec;
393
391
  nsec = ts.tv_nsec;
394
392
  } 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));
393
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
394
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
397
395
  }
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
396
 
403
397
  *b-- = '\0';
404
398
  if (withZone) {
@@ -464,8 +458,7 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
464
458
  b++;
465
459
  size = sizeof(buf) - (b - buf) - 1;
466
460
  assure_size(out, size);
467
- memcpy(out->cur, b, size);
468
- out->cur += size;
461
+ APPEND_CHARS(out->cur, b, size);
469
462
  *out->cur = '\0';
470
463
  }
471
464
 
@@ -485,20 +478,15 @@ void oj_dump_xml_time(VALUE obj, Out out) {
485
478
  int tzhour, tzmin;
486
479
  char tzsign = '+';
487
480
 
488
- #ifdef HAVE_RB_TIME_TIMESPEC
489
481
  if (16 <= sizeof(struct timespec)) {
490
482
  struct timespec ts = rb_time_timespec(obj);
491
483
 
492
484
  sec = ts.tv_sec;
493
485
  nsec = ts.tv_nsec;
494
486
  } 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));
487
+ sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
488
+ nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
497
489
  }
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
490
 
503
491
  assure_size(out, 36);
504
492
  if (9 > out->opts->sec_prec) {
@@ -535,14 +523,7 @@ void oj_dump_xml_time(VALUE obj, Out out) {
535
523
  }
536
524
  if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
537
525
  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);
526
+ int len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
546
527
  oj_dump_cstr(buf, len, 0, 0, out);
547
528
  } else {
548
529
  int len = sprintf(buf,
@@ -574,18 +555,7 @@ void oj_dump_xml_time(VALUE obj, Out out) {
574
555
  if (9 > out->opts->sec_prec) {
575
556
  format[32] = '0' + out->opts->sec_prec;
576
557
  }
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);
558
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec, tzsign, tzhour, tzmin);
589
559
  oj_dump_cstr(buf, len, 0, 0, out);
590
560
  }
591
561
  }
@@ -596,12 +566,8 @@ void oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
596
566
 
597
567
  void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
598
568
  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;
569
+ oj_out_init(out);
603
570
  }
604
- out->cur = out->buf;
605
571
  out->circ_cnt = 0;
606
572
  out->opts = copts;
607
573
  out->hash_cnt = 0;
@@ -636,38 +602,51 @@ void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int arg
636
602
  }
637
603
 
638
604
  void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
639
- char buf[4096];
640
605
  struct _out out;
641
606
  size_t size;
642
- FILE * f;
607
+ FILE *f;
643
608
  int ok;
644
609
 
645
- out.buf = buf;
646
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
647
- out.allocated = false;
610
+ oj_out_init(&out);
611
+
648
612
  out.omit_nil = copts->dump_opts.omit_nil;
649
613
  oj_dump_obj_to_json(obj, copts, &out);
650
614
  size = out.cur - out.buf;
651
615
  if (0 == (f = fopen(path, "w"))) {
652
- if (out.allocated) {
653
- xfree(out.buf);
654
- }
616
+ oj_out_free(&out);
655
617
  rb_raise(rb_eIOError, "%s", strerror(errno));
656
618
  }
657
619
  ok = (size == fwrite(out.buf, 1, size, f));
658
- if (out.allocated) {
659
- xfree(out.buf);
660
- }
661
- fclose(f);
620
+
621
+ oj_out_free(&out);
622
+
662
623
  if (!ok) {
663
624
  int err = ferror(f);
625
+ fclose(f);
664
626
 
665
627
  rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
666
628
  }
629
+ fclose(f);
667
630
  }
668
631
 
632
+ #if !IS_WINDOWS
633
+ static void write_ready(int fd) {
634
+ struct pollfd pp;
635
+ int i;
636
+
637
+ pp.fd = fd;
638
+ pp.events = POLLERR | POLLOUT;
639
+ pp.revents = 0;
640
+ if (0 >= (i = poll(&pp, 1, 5000))) {
641
+ if (0 == i || EAGAIN == errno) {
642
+ rb_raise(rb_eIOError, "write timed out");
643
+ }
644
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
645
+ }
646
+ }
647
+ #endif
648
+
669
649
  void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
670
- char buf[4096];
671
650
  struct _out out;
672
651
  ssize_t size;
673
652
  VALUE clas = rb_obj_class(stream);
@@ -676,41 +655,48 @@ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
676
655
  VALUE s;
677
656
  #endif
678
657
 
679
- out.buf = buf;
680
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
681
- out.allocated = false;
658
+ oj_out_init(&out);
659
+
682
660
  out.omit_nil = copts->dump_opts.omit_nil;
683
661
  oj_dump_obj_to_json(obj, copts, &out);
684
662
  size = out.cur - out.buf;
685
663
  if (oj_stringio_class == clas) {
686
664
  rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
687
665
  #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);
666
+ } else if (rb_respond_to(stream, oj_fileno_id) && Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
667
+ 0 != (fd = FIX2INT(s))) {
668
+ ssize_t cnt;
669
+ ssize_t total = 0;
670
+
671
+ while (true) {
672
+ if (0 > (cnt = write(fd, out.buf + total, size - total))) {
673
+ if (EAGAIN != errno) {
674
+ rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
675
+ break;
676
+ }
693
677
  }
694
- rb_raise(rb_eIOError, "Write failed. [%d:%s]", errno, strerror(errno));
678
+ total += cnt;
679
+ if (size <= total) {
680
+ // Completed
681
+ break;
682
+ }
683
+ write_ready(fd);
695
684
  }
696
685
  #endif
697
686
  } else if (rb_respond_to(stream, oj_write_id)) {
698
687
  rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
699
688
  } else {
700
- if (out.allocated) {
701
- xfree(out.buf);
702
- }
689
+ oj_out_free(&out);
703
690
  rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
704
691
  }
705
- if (out.allocated) {
706
- xfree(out.buf);
707
- }
692
+ oj_out_free(&out);
708
693
  }
709
694
 
710
695
  void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
711
- rb_encoding *enc = rb_enc_get(obj);
696
+ int idx = RB_ENCODING_GET(obj);
712
697
 
713
- if (oj_utf8_encoding != enc) {
698
+ if (oj_utf8_encoding_index != idx) {
699
+ rb_encoding *enc = rb_enc_from_index(idx);
714
700
  obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
715
701
  }
716
702
  oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
@@ -724,7 +710,7 @@ void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
724
710
 
725
711
  static void debug_raise(const char *orig, size_t cnt, int line) {
726
712
  char buf[1024];
727
- char * b = buf;
713
+ char *b = buf;
728
714
  const char *s = orig;
729
715
  const char *s_end = s + cnt;
730
716
 
@@ -750,11 +736,11 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
750
736
  } else {
751
737
  volatile VALUE jv;
752
738
 
753
- if (Yes == out->opts->trace) {
739
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
754
740
  oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
755
741
  }
756
742
  jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
757
- if (Yes == out->opts->trace) {
743
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
758
744
  oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
759
745
  }
760
746
  oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
@@ -763,7 +749,7 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
763
749
 
764
750
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
765
751
  size_t size;
766
- char * cmap;
752
+ char *cmap;
767
753
  const char *orig = str;
768
754
  bool has_hi = false;
769
755
 
@@ -776,6 +762,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
776
762
  cmap = ascii_friendly_chars;
777
763
  size = ascii_friendly_size((uint8_t *)str, cnt);
778
764
  break;
765
+ case SlashEsc:
766
+ has_hi = true;
767
+ cmap = slash_friendly_chars;
768
+ size = slash_friendly_size((uint8_t *)str, cnt);
769
+ break;
779
770
  case XSSEsc:
780
771
  cmap = xss_friendly_chars;
781
772
  size = xss_friendly_size((uint8_t *)str, cnt);
@@ -808,10 +799,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
808
799
  *out->cur++ = '"';
809
800
 
810
801
  if (escape1) {
811
- *out->cur++ = '\\';
812
- *out->cur++ = 'u';
813
- *out->cur++ = '0';
814
- *out->cur++ = '0';
802
+ APPEND_CHARS(out->cur, "\\u00", 4);
815
803
  dump_hex((uint8_t)*str, out);
816
804
  cnt--;
817
805
  size--;
@@ -822,8 +810,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
822
810
  if (is_sym) {
823
811
  *out->cur++ = ':';
824
812
  }
825
- memcpy(out->cur, str, cnt);
826
- out->cur += cnt;
813
+ APPEND_CHARS(out->cur, str, cnt);
827
814
  *out->cur++ = '"';
828
815
  } else {
829
816
  const char *end = str + cnt;
@@ -835,8 +822,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
835
822
  for (; str < end; str++) {
836
823
  switch (cmap[(uint8_t)*str]) {
837
824
  case '1':
838
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
839
- check_start <= str) {
825
+ if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && check_start <= str) {
840
826
  if (0 != (0x80 & (uint8_t)*str)) {
841
827
  if (0xC0 == (0xC0 & (uint8_t)*str)) {
842
828
  check_start = check_unicode(str, end, orig);
@@ -860,11 +846,9 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
860
846
  }
861
847
  break;
862
848
  case '3': // Unicode
863
- if (0xe2 == (uint8_t)*str &&
864
- (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
849
+ if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
865
850
  2 <= end - str) {
866
- if (0x80 == (uint8_t)str[1] &&
867
- (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
851
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
868
852
  str = dump_unicode(str, end, out, orig);
869
853
  } else {
870
854
  check_start = check_unicode(str, end, orig);
@@ -876,17 +860,12 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
876
860
  break;
877
861
  case '6': // control characters
878
862
  if (*(uint8_t *)str < 0x80) {
879
- *out->cur++ = '\\';
880
- *out->cur++ = 'u';
881
- *out->cur++ = '0';
882
- *out->cur++ = '0';
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);
@@ -960,15 +939,27 @@ void oj_dump_obj_to_s(VALUE obj, Out out) {
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
+ xfree(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) {
@@ -991,37 +982,28 @@ 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';
985
+ APPEND_CHARS(out->cur, "null", 4);
998
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';
991
+ APPEND_CHARS(out->cur, "true", 4);
1007
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';
997
+ APPEND_CHARS(out->cur, "false", 5);
1017
998
  *out->cur = '\0';
1018
999
  }
1019
1000
 
1020
1001
  void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1021
1002
  char buf[32];
1022
1003
  char * b = buf + sizeof(buf) - 1;
1023
- long long num = rb_num2ll(obj);
1004
+ long long num = NUM2LL(obj);
1024
1005
  int neg = 0;
1006
+ size_t cnt = 0;
1025
1007
  bool dump_as_string = false;
1026
1008
 
1027
1009
  if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
@@ -1052,10 +1034,9 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1052
1034
  if (dump_as_string) {
1053
1035
  *--b = '"';
1054
1036
  }
1055
- assure_size(out, (sizeof(buf) - (b - buf)));
1056
- for (; '\0' != *b; b++) {
1057
- *out->cur++ = *b;
1058
- }
1037
+ cnt = sizeof(buf) - (b - buf) - 1;
1038
+ assure_size(out, cnt);
1039
+ APPEND_CHARS(out->cur, b, cnt);
1059
1040
  *out->cur = '\0';
1060
1041
  }
1061
1042
 
@@ -1064,16 +1045,14 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1064
1045
  int cnt = (int)RSTRING_LEN(rs);
1065
1046
  bool dump_as_string = false;
1066
1047
 
1067
- if (out->opts->int_range_max != 0 ||
1068
- out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1048
+ if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
1069
1049
  dump_as_string = true;
1070
1050
  assure_size(out, cnt + 2);
1071
1051
  *out->cur++ = '"';
1072
1052
  } else {
1073
1053
  assure_size(out, cnt);
1074
1054
  }
1075
- memcpy(out->cur, RSTRING_PTR(rs), cnt);
1076
- out->cur += cnt;
1055
+ APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
1077
1056
  if (dump_as_string) {
1078
1057
  *out->cur++ = '"';
1079
1058
  }
@@ -1083,7 +1062,7 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1083
1062
  // Removed dependencies on math due to problems with CentOS 5.4.
1084
1063
  void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1085
1064
  char buf[64];
1086
- char * b;
1065
+ char *b;
1087
1066
  double d = rb_num2dbl(obj);
1088
1067
  int cnt = 0;
1089
1068
 
@@ -1162,7 +1141,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1162
1141
  } else if (isnan(d)) {
1163
1142
  if (ObjectMode == out->opts->mode) {
1164
1143
  strcpy(buf, nan_val);
1165
- cnt = sizeof(ninf_val) - 1;
1144
+ cnt = sizeof(nan_val) - 1;
1166
1145
  } else {
1167
1146
  NanDump nd = out->opts->dump_opts.nan_dump;
1168
1147
 
@@ -1206,9 +1185,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1206
1185
  cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
1207
1186
  }
1208
1187
  assure_size(out, cnt);
1209
- for (b = buf; '\0' != *b; b++) {
1210
- *out->cur++ = *b;
1211
- }
1188
+ APPEND_CHARS(out->cur, buf, cnt);
1212
1189
  *out->cur = '\0';
1213
1190
  }
1214
1191
 
@@ -1225,17 +1202,3 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
1225
1202
  }
1226
1203
  return cnt;
1227
1204
  }
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
- }