oj 3.13.11 → 3.15.0

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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -0
  3. data/README.md +4 -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 +17 -24
  10. data/ext/oj/code.h +2 -2
  11. data/ext/oj/compat.c +17 -44
  12. data/ext/oj/custom.c +70 -141
  13. data/ext/oj/debug.c +3 -9
  14. data/ext/oj/dump.c +128 -118
  15. data/ext/oj/dump.h +12 -8
  16. data/ext/oj/dump_compat.c +564 -641
  17. data/ext/oj/dump_leaf.c +17 -63
  18. data/ext/oj/dump_object.c +70 -199
  19. data/ext/oj/dump_strict.c +22 -46
  20. data/ext/oj/encoder.c +1 -1
  21. data/ext/oj/err.c +2 -13
  22. data/ext/oj/err.h +9 -12
  23. data/ext/oj/extconf.rb +14 -5
  24. data/ext/oj/fast.c +75 -103
  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 +75 -47
  30. data/ext/oj/object.c +49 -66
  31. data/ext/oj/odd.c +89 -67
  32. data/ext/oj/odd.h +15 -15
  33. data/ext/oj/oj.c +140 -99
  34. data/ext/oj/oj.h +80 -51
  35. data/ext/oj/parse.c +162 -184
  36. data/ext/oj/parse.h +7 -10
  37. data/ext/oj/parser.c +89 -34
  38. data/ext/oj/parser.h +18 -7
  39. data/ext/oj/rails.c +82 -146
  40. data/ext/oj/rails.h +1 -1
  41. data/ext/oj/reader.c +11 -12
  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 +20 -31
  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 -21
  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 +31 -68
  62. data/lib/oj/active_support_helper.rb +0 -1
  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 +4 -2
  67. data/lib/oj/mimic.rb +4 -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/Options.md +10 -0
  75. data/test/_test_active.rb +8 -9
  76. data/test/_test_active_mimic.rb +7 -8
  77. data/test/_test_mimic_rails.rb +17 -20
  78. data/test/activerecord/result_test.rb +5 -6
  79. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  80. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  81. data/test/{activesupport5 → activesupport7}/encoding_test.rb +20 -34
  82. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  83. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  84. data/test/files.rb +15 -15
  85. data/test/foo.rb +9 -71
  86. data/test/helper.rb +11 -8
  87. data/test/isolated/shared.rb +3 -2
  88. data/test/json_gem/json_addition_test.rb +2 -2
  89. data/test/json_gem/json_common_interface_test.rb +4 -4
  90. data/test/json_gem/json_encoding_test.rb +0 -0
  91. data/test/json_gem/json_ext_parser_test.rb +1 -0
  92. data/test/json_gem/json_fixtures_test.rb +3 -2
  93. data/test/json_gem/json_generator_test.rb +48 -36
  94. data/test/json_gem/json_generic_object_test.rb +11 -11
  95. data/test/json_gem/json_parser_test.rb +54 -47
  96. data/test/json_gem/json_string_matching_test.rb +9 -9
  97. data/test/json_gem/test_helper.rb +7 -3
  98. data/test/mem.rb +13 -12
  99. data/test/perf.rb +21 -26
  100. data/test/perf_compat.rb +31 -33
  101. data/test/perf_dump.rb +50 -0
  102. data/test/perf_fast.rb +80 -82
  103. data/test/perf_file.rb +27 -29
  104. data/test/perf_object.rb +65 -69
  105. data/test/perf_once.rb +12 -11
  106. data/test/perf_parser.rb +42 -48
  107. data/test/perf_saj.rb +46 -54
  108. data/test/perf_scp.rb +57 -69
  109. data/test/perf_simple.rb +41 -39
  110. data/test/perf_strict.rb +68 -70
  111. data/test/perf_wab.rb +67 -69
  112. data/test/prec.rb +3 -3
  113. data/test/sample/change.rb +0 -1
  114. data/test/sample/dir.rb +0 -1
  115. data/test/sample/doc.rb +0 -1
  116. data/test/sample/file.rb +0 -1
  117. data/test/sample/group.rb +0 -1
  118. data/test/sample/hasprops.rb +0 -1
  119. data/test/sample/layer.rb +0 -1
  120. data/test/sample/rect.rb +0 -1
  121. data/test/sample/shape.rb +0 -1
  122. data/test/sample/text.rb +0 -1
  123. data/test/sample.rb +16 -16
  124. data/test/sample_json.rb +8 -8
  125. data/test/test_compat.rb +76 -42
  126. data/test/test_custom.rb +72 -51
  127. data/test/test_debian.rb +7 -10
  128. data/test/test_fast.rb +86 -90
  129. data/test/test_file.rb +41 -30
  130. data/test/test_gc.rb +16 -5
  131. data/test/test_generate.rb +5 -5
  132. data/test/test_hash.rb +4 -4
  133. data/test/test_integer_range.rb +9 -9
  134. data/test/test_null.rb +20 -20
  135. data/test/test_object.rb +85 -96
  136. data/test/test_parser.rb +6 -22
  137. data/test/test_parser_debug.rb +27 -0
  138. data/test/test_parser_saj.rb +115 -23
  139. data/test/test_parser_usual.rb +6 -6
  140. data/test/test_rails.rb +2 -2
  141. data/test/test_saj.rb +10 -8
  142. data/test/test_scp.rb +37 -39
  143. data/test/test_strict.rb +30 -32
  144. data/test/test_various.rb +147 -99
  145. data/test/test_wab.rb +48 -44
  146. data/test/test_writer.rb +47 -47
  147. data/test/tests.rb +13 -4
  148. data/test/tests_mimic.rb +12 -3
  149. data/test/tests_mimic_addition.rb +12 -3
  150. metadata +33 -144
  151. data/test/activesupport4/decoding_test.rb +0 -108
  152. data/test/activesupport4/encoding_test.rb +0 -531
  153. data/test/activesupport4/test_helper.rb +0 -41
  154. data/test/activesupport5/test_helper.rb +0 -72
  155. data/test/bar.rb +0 -16
  156. data/test/baz.rb +0 -16
  157. data/test/bug.rb +0 -16
  158. data/test/zoo.rb +0 -13
data/ext/oj/debug.c CHANGED
@@ -30,9 +30,7 @@ static void add_int(struct _ojParser *p) {
30
30
  switch (p->stack[p->depth]) {
31
31
  case TOP_FUN: printf("*** add_int %lld at top\n", (long long)p->num.fixnum); break;
32
32
  case ARRAY_FUN: printf("*** add_int %lld to array\n", (long long)p->num.fixnum); break;
33
- case OBJECT_FUN:
34
- printf("*** add_int %lld with '%s'\n", (long long)p->num.fixnum, buf_str(&p->key));
35
- break;
33
+ case OBJECT_FUN: printf("*** add_int %lld with '%s'\n", (long long)p->num.fixnum, buf_str(&p->key)); break;
36
34
  }
37
35
  }
38
36
 
@@ -48,9 +46,7 @@ static void add_big(struct _ojParser *p) {
48
46
  switch (p->stack[p->depth]) {
49
47
  case TOP_FUN: printf("*** add_big %s at top\n", buf_str(&p->buf)); break;
50
48
  case ARRAY_FUN: printf("*** add_big %s to array\n", buf_str(&p->buf)); break;
51
- case OBJECT_FUN:
52
- printf("*** add_big %s with '%s'\n", buf_str(&p->buf), buf_str(&p->key));
53
- break;
49
+ case OBJECT_FUN: printf("*** add_big %s with '%s'\n", buf_str(&p->buf), buf_str(&p->key)); break;
54
50
  }
55
51
  }
56
52
 
@@ -58,9 +54,7 @@ static void add_str(struct _ojParser *p) {
58
54
  switch (p->stack[p->depth]) {
59
55
  case TOP_FUN: printf("*** add_str '%s' at top\n", buf_str(&p->buf)); break;
60
56
  case ARRAY_FUN: printf("*** add_str '%s' to array\n", buf_str(&p->buf)); break;
61
- case OBJECT_FUN:
62
- printf("*** add_str '%s' with '%s'\n", buf_str(&p->buf), buf_str(&p->key));
63
- break;
57
+ case OBJECT_FUN: printf("*** add_str '%s' with '%s'\n", buf_str(&p->buf), buf_str(&p->key)); break;
64
58
  }
65
59
  }
66
60
 
data/ext/oj/dump.c CHANGED
@@ -15,6 +15,7 @@
15
15
  #endif
16
16
 
17
17
  #include "cache8.h"
18
+ #include "mem.h"
18
19
  #include "odd.h"
19
20
  #include "oj.h"
20
21
  #include "trace.h"
@@ -32,6 +33,7 @@ static const char nan_val[] = NAN_VAL;
32
33
  typedef unsigned long ulong;
33
34
 
34
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);
35
37
  static size_t xss_friendly_size(const uint8_t *str, size_t len);
36
38
  static size_t ascii_friendly_size(const uint8_t *str, size_t len);
37
39
 
@@ -59,6 +61,17 @@ static char hibit_friendly_chars[256] = "\
59
61
  11111111111111111111111111111111\
60
62
  11111111111111111111111111111111";
61
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
+
62
75
  // High bit set characters are always encoded as unicode. Worse case is 3
63
76
  // bytes per character in the output. That makes this conservative.
64
77
  static char ascii_friendly_chars[256] = "\
@@ -143,6 +156,10 @@ inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
143
156
  return calculate_string_size(str, len, hibit_friendly_chars);
144
157
  }
145
158
 
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);
161
+ }
162
+
146
163
  inline static size_t ascii_friendly_size(const uint8_t *str, size_t len) {
147
164
  return calculate_string_size(str, len, ascii_friendly_chars);
148
165
  }
@@ -234,7 +251,7 @@ inline static void dump_hex(uint8_t c, Out out) {
234
251
  static void raise_invalid_unicode(const char *str, int len, int pos) {
235
252
  char c;
236
253
  char code[32];
237
- char * cp = code;
254
+ char *cp = code;
238
255
  int i;
239
256
  uint8_t d;
240
257
 
@@ -289,16 +306,14 @@ static const char *dump_unicode(const char *str, const char *end, Out out, const
289
306
  uint32_t c1;
290
307
 
291
308
  code -= 0x00010000;
292
- c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
293
- code = (code & 0x000003FF) + 0x0000DC00;
294
- *out->cur++ = '\\';
295
- *out->cur++ = 'u';
309
+ c1 = ((code >> 10) & 0x000003FF) + 0x0000D800;
310
+ code = (code & 0x000003FF) + 0x0000DC00;
311
+ APPEND_CHARS(out->cur, "\\u", 2);
296
312
  for (i = 3; 0 <= i; i--) {
297
313
  *out->cur++ = hex_chars[(uint8_t)(c1 >> (i * 4)) & 0x0F];
298
314
  }
299
315
  }
300
- *out->cur++ = '\\';
301
- *out->cur++ = 'u';
316
+ APPEND_CHARS(out->cur, "\\u", 2);
302
317
  for (i = 3; 0 <= i; i--) {
303
318
  *out->cur++ = hex_chars[(uint8_t)(code >> (i * 4)) & 0x0F];
304
319
  }
@@ -347,9 +362,7 @@ long oj_check_circular(VALUE obj, Out out) {
347
362
  } else {
348
363
  if (ObjectMode == out->opts->mode) {
349
364
  assure_size(out, 18);
350
- *out->cur++ = '"';
351
- *out->cur++ = '^';
352
- *out->cur++ = 'r';
365
+ APPEND_CHARS(out->cur, "\"^r", 3);
353
366
  dump_ulong(id, out);
354
367
  *out->cur++ = '"';
355
368
  }
@@ -361,15 +374,14 @@ long oj_check_circular(VALUE obj, Out out) {
361
374
 
362
375
  void oj_dump_time(VALUE obj, Out out, int withZone) {
363
376
  char buf[64];
364
- char * b = buf + sizeof(buf) - 1;
377
+ char *b = buf + sizeof(buf) - 1;
365
378
  long size;
366
- char * dot;
379
+ char *dot;
367
380
  int neg = 0;
368
381
  long one = 1000000000;
369
382
  long long sec;
370
383
  long long nsec;
371
384
 
372
- #ifdef HAVE_RB_TIME_TIMESPEC
373
385
  // rb_time_timespec as well as rb_time_timeeval have a bug that causes an
374
386
  // exception to be raised if a time is before 1970 on 32 bit systems so
375
387
  // check the timespec size and use the ruby calls if a 32 bit system.
@@ -379,13 +391,9 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
379
391
  sec = (long long)ts.tv_sec;
380
392
  nsec = ts.tv_nsec;
381
393
  } else {
382
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
383
- 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));
384
396
  }
385
- #else
386
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
387
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
388
- #endif
389
397
 
390
398
  *b-- = '\0';
391
399
  if (withZone) {
@@ -451,13 +459,12 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
451
459
  b++;
452
460
  size = sizeof(buf) - (b - buf) - 1;
453
461
  assure_size(out, size);
454
- memcpy(out->cur, b, size);
455
- out->cur += size;
462
+ APPEND_CHARS(out->cur, b, size);
456
463
  *out->cur = '\0';
457
464
  }
458
465
 
459
466
  void oj_dump_ruby_time(VALUE obj, Out out) {
460
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
467
+ volatile VALUE rstr = oj_safe_string_convert(obj);
461
468
 
462
469
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
463
470
  }
@@ -472,20 +479,15 @@ void oj_dump_xml_time(VALUE obj, Out out) {
472
479
  int tzhour, tzmin;
473
480
  char tzsign = '+';
474
481
 
475
- #ifdef HAVE_RB_TIME_TIMESPEC
476
482
  if (16 <= sizeof(struct timespec)) {
477
483
  struct timespec ts = rb_time_timespec(obj);
478
484
 
479
485
  sec = ts.tv_sec;
480
486
  nsec = ts.tv_nsec;
481
487
  } else {
482
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
483
- 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));
484
490
  }
485
- #else
486
- sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
487
- nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
488
- #endif
489
491
 
490
492
  assure_size(out, 36);
491
493
  if (9 > out->opts->sec_prec) {
@@ -565,12 +567,8 @@ void oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
565
567
 
566
568
  void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
567
569
  if (0 == out->buf) {
568
- out->buf = ALLOC_N(char, 4096);
569
- // 1 less than end plus extra for possible errors
570
- out->end = out->buf + 4095 - BUFFER_EXTRA;
571
- out->allocated = true;
570
+ oj_out_init(out);
572
571
  }
573
- out->cur = out->buf;
574
572
  out->circ_cnt = 0;
575
573
  out->opts = copts;
576
574
  out->hash_cnt = 0;
@@ -605,36 +603,34 @@ void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int arg
605
603
  }
606
604
 
607
605
  void oj_write_obj_to_file(VALUE obj, const char *path, Options copts) {
608
- char buf[4096];
609
606
  struct _out out;
610
607
  size_t size;
611
- FILE * f;
608
+ FILE *f;
612
609
  int ok;
613
610
 
614
- out.buf = buf;
615
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
616
- out.allocated = false;
617
- out.omit_nil = copts->dump_opts.omit_nil;
611
+ oj_out_init(&out);
612
+
613
+ out.omit_nil = copts->dump_opts.omit_nil;
618
614
  oj_dump_obj_to_json(obj, copts, &out);
619
615
  size = out.cur - out.buf;
620
616
  if (0 == (f = fopen(path, "w"))) {
621
- if (out.allocated) {
622
- xfree(out.buf);
623
- }
617
+ oj_out_free(&out);
624
618
  rb_raise(rb_eIOError, "%s", strerror(errno));
625
619
  }
626
620
  ok = (size == fwrite(out.buf, 1, size, f));
627
- if (out.allocated) {
628
- xfree(out.buf);
629
- }
630
- fclose(f);
621
+
622
+ oj_out_free(&out);
623
+
631
624
  if (!ok) {
632
625
  int err = ferror(f);
626
+ fclose(f);
633
627
 
634
628
  rb_raise(rb_eIOError, "Write failed. [%d:%s]", err, strerror(err));
635
629
  }
630
+ fclose(f);
636
631
  }
637
632
 
633
+ #if !IS_WINDOWS
638
634
  static void write_ready(int fd) {
639
635
  struct pollfd pp;
640
636
  int i;
@@ -649,9 +645,9 @@ static void write_ready(int fd) {
649
645
  rb_raise(rb_eIOError, "write failed. %d %s.", errno, strerror(errno));
650
646
  }
651
647
  }
648
+ #endif
652
649
 
653
650
  void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
654
- char buf[4096];
655
651
  struct _out out;
656
652
  ssize_t size;
657
653
  VALUE clas = rb_obj_class(stream);
@@ -660,10 +656,9 @@ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
660
656
  VALUE s;
661
657
  #endif
662
658
 
663
- out.buf = buf;
664
- out.end = buf + sizeof(buf) - BUFFER_EXTRA;
665
- out.allocated = false;
666
- out.omit_nil = copts->dump_opts.omit_nil;
659
+ oj_out_init(&out);
660
+
661
+ out.omit_nil = copts->dump_opts.omit_nil;
667
662
  oj_dump_obj_to_json(obj, copts, &out);
668
663
  size = out.cur - out.buf;
669
664
  if (oj_stringio_class == clas) {
@@ -692,21 +687,18 @@ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) {
692
687
  } else if (rb_respond_to(stream, oj_write_id)) {
693
688
  rb_funcall(stream, oj_write_id, 1, rb_str_new(out.buf, size));
694
689
  } else {
695
- if (out.allocated) {
696
- xfree(out.buf);
697
- }
690
+ oj_out_free(&out);
698
691
  rb_raise(rb_eArgError, "to_stream() expected an IO Object.");
699
692
  }
700
- if (out.allocated) {
701
- xfree(out.buf);
702
- }
693
+ oj_out_free(&out);
703
694
  }
704
695
 
705
696
  void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
706
- rb_encoding *enc = rb_enc_get(obj);
697
+ int idx = RB_ENCODING_GET(obj);
707
698
 
708
- if (oj_utf8_encoding != enc) {
709
- 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);
710
702
  }
711
703
  oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
712
704
  }
@@ -719,7 +711,7 @@ void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
719
711
 
720
712
  static void debug_raise(const char *orig, size_t cnt, int line) {
721
713
  char buf[1024];
722
- char * b = buf;
714
+ char *b = buf;
723
715
  const char *s = orig;
724
716
  const char *s_end = s + cnt;
725
717
 
@@ -745,20 +737,16 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
745
737
  } else {
746
738
  volatile VALUE jv;
747
739
 
748
- if (Yes == out->opts->trace) {
749
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
750
- }
740
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyIn);
751
741
  jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
752
- if (Yes == out->opts->trace) {
753
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
754
- }
742
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyOut);
755
743
  oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
756
744
  }
757
745
  }
758
746
 
759
747
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
760
748
  size_t size;
761
- char * cmap;
749
+ char *cmap;
762
750
  const char *orig = str;
763
751
  bool has_hi = false;
764
752
 
@@ -771,6 +759,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
771
759
  cmap = ascii_friendly_chars;
772
760
  size = ascii_friendly_size((uint8_t *)str, cnt);
773
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;
774
767
  case XSSEsc:
775
768
  cmap = xss_friendly_chars;
776
769
  size = xss_friendly_size((uint8_t *)str, cnt);
@@ -803,10 +796,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
803
796
  *out->cur++ = '"';
804
797
 
805
798
  if (escape1) {
806
- *out->cur++ = '\\';
807
- *out->cur++ = 'u';
808
- *out->cur++ = '0';
809
- *out->cur++ = '0';
799
+ APPEND_CHARS(out->cur, "\\u00", 4);
810
800
  dump_hex((uint8_t)*str, out);
811
801
  cnt--;
812
802
  size--;
@@ -817,8 +807,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
817
807
  if (is_sym) {
818
808
  *out->cur++ = ':';
819
809
  }
820
- memcpy(out->cur, str, cnt);
821
- out->cur += cnt;
810
+ APPEND_CHARS(out->cur, str, cnt);
822
811
  *out->cur++ = '"';
823
812
  } else {
824
813
  const char *end = str + cnt;
@@ -868,10 +857,10 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
868
857
  break;
869
858
  case '6': // control characters
870
859
  if (*(uint8_t *)str < 0x80) {
871
- *out->cur++ = '\\';
872
- *out->cur++ = 'u';
873
- *out->cur++ = '0';
874
- *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);
875
864
  dump_hex((uint8_t)*str, out);
876
865
  } else {
877
866
  if (0xe2 == (uint8_t)*str &&
@@ -943,31 +932,43 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
943
932
  }
944
933
 
945
934
  void oj_dump_obj_to_s(VALUE obj, Out out) {
946
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
935
+ volatile VALUE rstr = oj_safe_string_convert(obj);
947
936
 
948
937
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
949
938
  }
950
939
 
951
940
  void oj_dump_raw(const char *str, size_t cnt, Out out) {
952
941
  assure_size(out, cnt + 10);
953
- memcpy(out->cur, str, cnt);
954
- out->cur += cnt;
942
+ APPEND_CHARS(out->cur, str, cnt);
955
943
  *out->cur = '\0';
956
944
  }
957
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
+
958
959
  void oj_grow_out(Out out, size_t len) {
959
960
  size_t size = out->end - out->buf;
960
961
  long pos = out->cur - out->buf;
961
- char * buf = out->buf;
962
+ char *buf = out->buf;
962
963
 
963
964
  size *= 2;
964
965
  if (size <= len * 2 + pos) {
965
966
  size += len;
966
967
  }
967
968
  if (out->allocated) {
968
- REALLOC_N(buf, char, (size + BUFFER_EXTRA));
969
+ OJ_R_REALLOC_N(buf, char, (size + BUFFER_EXTRA));
969
970
  } else {
970
- buf = ALLOC_N(char, (size + BUFFER_EXTRA));
971
+ buf = OJ_R_ALLOC_N(char, (size + BUFFER_EXTRA));
971
972
  out->allocated = true;
972
973
  memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
973
974
  }
@@ -981,37 +982,40 @@ void oj_grow_out(Out out, size_t len) {
981
982
 
982
983
  void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok) {
983
984
  assure_size(out, 4);
984
- *out->cur++ = 'n';
985
- *out->cur++ = 'u';
986
- *out->cur++ = 'l';
987
- *out->cur++ = 'l';
988
- *out->cur = '\0';
985
+ APPEND_CHARS(out->cur, "null", 4);
986
+ *out->cur = '\0';
989
987
  }
990
988
 
991
989
  void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok) {
992
990
  assure_size(out, 4);
993
- *out->cur++ = 't';
994
- *out->cur++ = 'r';
995
- *out->cur++ = 'u';
996
- *out->cur++ = 'e';
997
- *out->cur = '\0';
991
+ APPEND_CHARS(out->cur, "true", 4);
992
+ *out->cur = '\0';
998
993
  }
999
994
 
1000
995
  void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
1001
996
  assure_size(out, 5);
1002
- *out->cur++ = 'f';
1003
- *out->cur++ = 'a';
1004
- *out->cur++ = 'l';
1005
- *out->cur++ = 's';
1006
- *out->cur++ = 'e';
1007
- *out->cur = '\0';
997
+ APPEND_CHARS(out->cur, "false", 5);
998
+ *out->cur = '\0';
1008
999
  }
1009
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
+
1010
1013
  void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1011
1014
  char buf[32];
1012
- char * b = buf + sizeof(buf) - 1;
1013
- long long num = rb_num2ll(obj);
1015
+ char *b = buf + sizeof(buf) - 1;
1016
+ long long num = NUM2LL(obj);
1014
1017
  int neg = 0;
1018
+ size_t cnt = 0;
1015
1019
  bool dump_as_string = false;
1016
1020
 
1017
1021
  if (out->opts->int_range_max != 0 && out->opts->int_range_min != 0 &&
@@ -1028,9 +1032,19 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1028
1032
  *b-- = '"';
1029
1033
  }
1030
1034
  if (0 < num) {
1031
- for (; 0 < num; num /= 10, b--) {
1032
- *b = (num % 10) + '0';
1035
+ while (100 <= num) {
1036
+ unsigned idx = num % 100 * 2;
1037
+ *b-- = digits_table[idx + 1];
1038
+ *b-- = digits_table[idx];
1039
+ num /= 100;
1040
+ }
1041
+ if (num < 10) {
1042
+ *b-- = num + '0';
1043
+ } else {
1044
+ *b-- = digits_table[num * 2 + 1];
1045
+ *b-- = digits_table[num * 2];
1033
1046
  }
1047
+
1034
1048
  if (neg) {
1035
1049
  *b = '-';
1036
1050
  } else {
@@ -1042,10 +1056,9 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1042
1056
  if (dump_as_string) {
1043
1057
  *--b = '"';
1044
1058
  }
1045
- assure_size(out, (sizeof(buf) - (b - buf)));
1046
- for (; '\0' != *b; b++) {
1047
- *out->cur++ = *b;
1048
- }
1059
+ cnt = sizeof(buf) - (b - buf) - 1;
1060
+ assure_size(out, cnt);
1061
+ APPEND_CHARS(out->cur, b, cnt);
1049
1062
  *out->cur = '\0';
1050
1063
  }
1051
1064
 
@@ -1061,8 +1074,7 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1061
1074
  } else {
1062
1075
  assure_size(out, cnt);
1063
1076
  }
1064
- memcpy(out->cur, RSTRING_PTR(rs), cnt);
1065
- out->cur += cnt;
1077
+ APPEND_CHARS(out->cur, RSTRING_PTR(rs), cnt);
1066
1078
  if (dump_as_string) {
1067
1079
  *out->cur++ = '"';
1068
1080
  }
@@ -1072,7 +1084,7 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1072
1084
  // Removed dependencies on math due to problems with CentOS 5.4.
1073
1085
  void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1074
1086
  char buf[64];
1075
- char * b;
1087
+ char *b;
1076
1088
  double d = rb_num2dbl(obj);
1077
1089
  int cnt = 0;
1078
1090
 
@@ -1151,7 +1163,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1151
1163
  } else if (isnan(d)) {
1152
1164
  if (ObjectMode == out->opts->mode) {
1153
1165
  strcpy(buf, nan_val);
1154
- cnt = sizeof(ninf_val) - 1;
1166
+ cnt = sizeof(nan_val) - 1;
1155
1167
  } else {
1156
1168
  NanDump nd = out->opts->dump_opts.nan_dump;
1157
1169
 
@@ -1183,7 +1195,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1183
1195
  } else if (d == (double)(long long int)d) {
1184
1196
  cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1185
1197
  } else if (0 == out->opts->float_prec) {
1186
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1198
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1187
1199
 
1188
1200
  cnt = (int)RSTRING_LEN(rstr);
1189
1201
  if ((int)sizeof(buf) <= cnt) {
@@ -1195,9 +1207,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1195
1207
  cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
1196
1208
  }
1197
1209
  assure_size(out, cnt);
1198
- for (b = buf; '\0' != *b; b++) {
1199
- *out->cur++ = *b;
1200
- }
1210
+ APPEND_CHARS(out->cur, buf, cnt);
1201
1211
  *out->cur = '\0';
1202
1212
  }
1203
1213
 
@@ -1207,7 +1217,7 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
1207
1217
  // Round off issues at 16 significant digits so check for obvious ones of
1208
1218
  // 0001 and 9999.
1209
1219
  if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
1210
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1220
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1211
1221
 
1212
1222
  strcpy(buf, RSTRING_PTR(rstr));
1213
1223
  cnt = (int)RSTRING_LEN(rstr);
data/ext/oj/dump.h CHANGED
@@ -32,6 +32,11 @@ extern void oj_dump_obj_to_s(VALUE obj, Out out);
32
32
 
33
33
  extern const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp);
34
34
 
35
+ // initialize an out buffer with the provided stack allocated memory
36
+ extern void oj_out_init(Out out);
37
+ // clean up the out buffer if it uses heap allocated memory
38
+ extern void oj_out_free(Out out);
39
+
35
40
  extern void oj_grow_out(Out out, size_t len);
36
41
  extern long oj_check_circular(VALUE obj, Out out);
37
42
 
@@ -62,9 +67,8 @@ inline static void fill_indent(Out out, int cnt) {
62
67
  if (0 < out->indent) {
63
68
  cnt *= out->indent;
64
69
  *out->cur++ = '\n';
65
- for (; 0 < cnt; cnt--) {
66
- *out->cur++ = ' ';
67
- }
70
+ memset(out->cur, ' ', cnt);
71
+ out->cur += cnt;
68
72
  }
69
73
  }
70
74
 
@@ -83,8 +87,9 @@ inline static bool dump_ignore(Options opts, VALUE obj) {
83
87
  }
84
88
 
85
89
  inline static void dump_ulong(unsigned long num, Out out) {
86
- char buf[32];
87
- char *b = buf + sizeof(buf) - 1;
90
+ char buf[32];
91
+ char *b = buf + sizeof(buf) - 1;
92
+ size_t cnt = 0;
88
93
 
89
94
  *b-- = '\0';
90
95
  if (0 < num) {
@@ -95,9 +100,8 @@ inline static void dump_ulong(unsigned long num, Out out) {
95
100
  } else {
96
101
  *b = '0';
97
102
  }
98
- for (; '\0' != *b; b++) {
99
- *out->cur++ = *b;
100
- }
103
+ cnt = sizeof(buf) - (b - buf) - 1;
104
+ APPEND_CHARS(out->cur, b, cnt);
101
105
  *out->cur = '\0';
102
106
  }
103
107