oj 3.14.3 → 3.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/ext/oj/code.c +3 -10
  4. data/ext/oj/compat.c +5 -18
  5. data/ext/oj/custom.c +10 -28
  6. data/ext/oj/dump.c +40 -10
  7. data/ext/oj/dump.h +1 -4
  8. data/ext/oj/extconf.rb +4 -2
  9. data/ext/oj/fast.c +3 -6
  10. data/ext/oj/mimic_json.c +21 -1
  11. data/ext/oj/object.c +7 -21
  12. data/ext/oj/oj.c +24 -4
  13. data/ext/oj/oj.h +10 -6
  14. data/ext/oj/parse.c +3 -5
  15. data/ext/oj/parse.h +16 -14
  16. data/ext/oj/parser.h +2 -2
  17. data/ext/oj/reader.c +1 -3
  18. data/ext/oj/saj.c +1 -1
  19. data/ext/oj/strict.c +9 -27
  20. data/ext/oj/wab.c +9 -27
  21. data/lib/oj/active_support_helper.rb +2 -3
  22. data/lib/oj/json.rb +156 -149
  23. data/lib/oj/mimic.rb +3 -1
  24. data/lib/oj/version.rb +1 -1
  25. data/lib/oj.rb +3 -0
  26. data/pages/Options.md +4 -0
  27. data/test/_test_active.rb +8 -8
  28. data/test/_test_active_mimic.rb +7 -7
  29. data/test/_test_mimic_rails.rb +17 -19
  30. data/test/files.rb +14 -14
  31. data/test/foo.rb +15 -10
  32. data/test/helper.rb +4 -4
  33. data/test/mem.rb +8 -7
  34. data/test/perf.rb +21 -26
  35. data/test/perf_compat.rb +30 -32
  36. data/test/perf_dump.rb +27 -27
  37. data/test/perf_fast.rb +80 -82
  38. data/test/perf_file.rb +27 -29
  39. data/test/perf_object.rb +65 -68
  40. data/test/perf_once.rb +8 -7
  41. data/test/perf_parser.rb +40 -46
  42. data/test/perf_saj.rb +46 -53
  43. data/test/perf_scp.rb +57 -69
  44. data/test/perf_simple.rb +40 -38
  45. data/test/perf_strict.rb +68 -70
  46. data/test/perf_wab.rb +67 -69
  47. data/test/prec.rb +3 -3
  48. data/test/sample.rb +16 -15
  49. data/test/sample_json.rb +8 -7
  50. data/test/test_compat.rb +44 -46
  51. data/test/test_custom.rb +56 -42
  52. data/test/test_debian.rb +6 -9
  53. data/test/test_fast.rb +78 -72
  54. data/test/test_file.rb +16 -21
  55. data/test/test_gc.rb +5 -5
  56. data/test/test_generate.rb +5 -5
  57. data/test/test_hash.rb +4 -4
  58. data/test/test_integer_range.rb +9 -9
  59. data/test/test_null.rb +18 -20
  60. data/test/test_object.rb +76 -86
  61. data/test/test_parser.rb +4 -4
  62. data/test/test_parser_debug.rb +4 -4
  63. data/test/test_parser_saj.rb +31 -31
  64. data/test/test_parser_usual.rb +3 -3
  65. data/test/test_rails.rb +2 -2
  66. data/test/test_saj.rb +8 -8
  67. data/test/test_scp.rb +29 -29
  68. data/test/test_strict.rb +25 -31
  69. data/test/test_various.rb +121 -75
  70. data/test/test_wab.rb +43 -42
  71. data/test/test_writer.rb +46 -46
  72. data/test/tests.rb +7 -7
  73. data/test/tests_mimic.rb +6 -6
  74. data/test/tests_mimic_addition.rb +6 -6
  75. metadata +3 -6
  76. data/test/bar.rb +0 -11
  77. data/test/baz.rb +0 -16
  78. data/test/bug.rb +0 -16
  79. data/test/zoo.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce52cbfa0a36f659877cda70d0376fdc19264f96ff8ff1f8085e8cb3560c8bae
4
- data.tar.gz: 44b94e3e4174f1464dd7b4f025e28110dc273a353c42a4d8460c6e45846d1fab
3
+ metadata.gz: 257582096951b3c4db4b4979c49afd542ae855ba454bd6d51dcb1a69f68c2405
4
+ data.tar.gz: d835a958d019f7034e93d3f97ca1829fad74a4e26743f6a8b2b581c03ac9f14a
5
5
  SHA512:
6
- metadata.gz: 465dd87a8c8e11b562de10d1ab60d78f8e2250c4c06f997455673ea67fb88c75e1c07dea01c01fa864ebfa39a2b0bee1371ee7551895ba6d125bee3af20975ee
7
- data.tar.gz: 430a1f0a293fe87fd03af88b2e1f27e70ffae7b7ab8a2451dea1de5891ce6b9967f5475bd33f7cb37c728a7e4ed44b2b3c5051ebeb452c76f393f543a42d5dd1
6
+ metadata.gz: 374e55547de2db4b2199c05bebe5fa74953021883f7f609e328466c076016817b0cb8a7be03ead9c68b3fe4895eb5de0bcd60e1384ff7f1a5f7cd480824faba0
7
+ data.tar.gz: ec9cf41116602a51c49ff73ba6fc39ecbb501d51cbab0a6065bb38d2ac15a7f42332dc66f0d97df40bea6da5fafbc15209d06039180829c4c4b299b0e138676e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.15.1 - 2023-07-30
4
+
5
+ - Add protection against some using `require 'oj/json`, an internal file.
6
+
7
+ - Fixed non-json errors when in compat mode.
8
+
9
+ ## 3.15.0 - 2023-06-02
10
+
11
+ - Added `omit_null_byte` option when dumping.
12
+
3
13
  ## 3.14.3 - 2023-04-07
4
14
 
5
15
  - Fixed compat parse with optimized Hash when parsing a JSON::GenericObject.
data/ext/oj/code.c CHANGED
@@ -185,24 +185,17 @@ void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out, bool with_class) {
185
185
  } else {
186
186
  char buf[32];
187
187
  char *b = buf + sizeof(buf) - 1;
188
- int neg = 0;
188
+ bool neg = false;
189
189
  long num = attrs->num;
190
190
  size_t cnt = 0;
191
191
 
192
192
  if (0 > num) {
193
- neg = 1;
193
+ neg = true;
194
194
  num = -num;
195
195
  }
196
196
  *b-- = '\0';
197
197
  if (0 < num) {
198
- for (; 0 < num; num /= 10, b--) {
199
- *b = (num % 10) + '0';
200
- }
201
- if (neg) {
202
- *b = '-';
203
- } else {
204
- b++;
205
- }
198
+ b = oj_longlong_to_string(num, neg, b);
206
199
  } else {
207
200
  *b = '0';
208
201
  }
data/ext/oj/compat.c CHANGED
@@ -13,32 +13,19 @@
13
13
  #include "trace.h"
14
14
 
15
15
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
16
- const char *key = kval->key;
17
- int klen = kval->klen;
18
- Val parent = stack_peek(&pi->stack);
19
- volatile VALUE rkey = kval->key_val;
16
+ const char *key = kval->key;
17
+ int klen = kval->klen;
18
+ Val parent = stack_peek(&pi->stack);
20
19
 
21
- if (Qundef == rkey && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
20
+ if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
22
21
  *pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
23
22
  0 == strncmp(pi->options.create_id, key, klen)) {
24
23
  parent->classname = oj_strndup(str, len);
25
24
  parent->clen = len;
26
25
  } else {
27
26
  volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
27
+ volatile VALUE rkey = oj_calc_hash_key(pi, kval);
28
28
 
29
- if (Qundef == rkey) {
30
- if (Yes != pi->options.cache_keys) {
31
- if (Yes == pi->options.sym_key) {
32
- rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
33
- } else {
34
- rkey = rb_utf8_str_new(key, klen);
35
- }
36
- } else if (Yes == pi->options.sym_key) {
37
- rkey = oj_sym_intern(key, klen);
38
- } else {
39
- rkey = oj_str_intern(key, klen);
40
- }
41
- }
42
29
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
43
30
  VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
44
31
 
data/ext/oj/custom.c CHANGED
@@ -889,12 +889,11 @@ void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
889
889
  ///// load functions /////
890
890
 
891
891
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
892
- const char *key = kval->key;
893
- int klen = kval->klen;
894
- Val parent = stack_peek(&pi->stack);
895
- volatile VALUE rkey = kval->key_val;
892
+ const char *key = kval->key;
893
+ int klen = kval->klen;
894
+ Val parent = stack_peek(&pi->stack);
896
895
 
897
- if (Qundef == rkey && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
896
+ if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
898
897
  *pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
899
898
  0 == strncmp(pi->options.create_id, key, klen)) {
900
899
  parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
@@ -907,15 +906,8 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
907
906
  }
908
907
  } else {
909
908
  volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
910
- // volatile VALUE rstr = rb_utf8_str_new(str, len);
909
+ volatile VALUE rkey = oj_calc_hash_key(pi, kval);
911
910
 
912
- if (Qundef == rkey) {
913
- if (Yes == pi->options.sym_key) {
914
- rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
915
- } else {
916
- rkey = rb_utf8_str_new(key, klen);
917
- }
918
- }
919
911
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
920
912
  VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
921
913
 
@@ -937,9 +929,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
937
929
  break;
938
930
  default: break;
939
931
  }
940
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
941
- oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rstr);
942
- }
932
+ TRACE_PARSE_CALL(pi->options.trace, "set_string", pi, rstr);
943
933
  }
944
934
  }
945
935
 
@@ -998,9 +988,7 @@ static void hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
998
988
  break;
999
989
  default: break;
1000
990
  }
1001
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
1002
- oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
1003
- }
991
+ TRACE_PARSE_CALL(pi->options.trace, "set_string", pi, rval);
1004
992
  }
1005
993
 
1006
994
  static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
@@ -1011,9 +999,7 @@ static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
1011
999
  case T_HASH: rb_hash_aset(parent->val, oj_calc_hash_key(pi, kval), value); break;
1012
1000
  default: break;
1013
1001
  }
1014
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
1015
- oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
1016
- }
1002
+ TRACE_PARSE_CALL(pi->options.trace, "set_value", pi, value);
1017
1003
  }
1018
1004
 
1019
1005
  static void array_append_num(ParseInfo pi, NumInfo ni) {
@@ -1021,9 +1007,7 @@ static void array_append_num(ParseInfo pi, NumInfo ni) {
1021
1007
  volatile VALUE rval = oj_num_as_value(ni);
1022
1008
 
1023
1009
  rb_ary_push(parent->val, rval);
1024
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
1025
- oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
1026
- }
1010
+ TRACE_PARSE_CALL(pi->options.trace, "append_number", pi, rval);
1027
1011
  }
1028
1012
 
1029
1013
  static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
@@ -1038,9 +1022,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
1038
1022
  }
1039
1023
  }
1040
1024
  rb_ary_push(stack_peek(&pi->stack)->val, rstr);
1041
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
1042
- oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rstr);
1043
- }
1025
+ TRACE_PARSE_CALL(pi->options.trace, "append_string", pi, rstr);
1044
1026
  }
1045
1027
 
1046
1028
  void oj_set_custom_callbacks(ParseInfo pi) {
data/ext/oj/dump.c CHANGED
@@ -857,6 +857,9 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
857
857
  break;
858
858
  case '6': // control characters
859
859
  if (*(uint8_t *)str < 0x80) {
860
+ if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
861
+ break;
862
+ }
860
863
  APPEND_CHARS(out->cur, "\\u00", 4);
861
864
  dump_hex((uint8_t)*str, out);
862
865
  } else {
@@ -995,11 +998,45 @@ void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok) {
995
998
  *out->cur = '\0';
996
999
  }
997
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;
1033
+ }
1034
+
998
1035
  void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
999
1036
  char buf[32];
1000
1037
  char *b = buf + sizeof(buf) - 1;
1001
1038
  long long num = NUM2LL(obj);
1002
- int neg = 0;
1039
+ bool neg = false;
1003
1040
  size_t cnt = 0;
1004
1041
  bool dump_as_string = false;
1005
1042
 
@@ -1008,7 +1045,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1008
1045
  dump_as_string = true;
1009
1046
  }
1010
1047
  if (0 > num) {
1011
- neg = 1;
1048
+ neg = true;
1012
1049
  num = -num;
1013
1050
  }
1014
1051
  *b-- = '\0';
@@ -1017,14 +1054,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1017
1054
  *b-- = '"';
1018
1055
  }
1019
1056
  if (0 < num) {
1020
- for (; 0 < num; num /= 10, b--) {
1021
- *b = (num % 10) + '0';
1022
- }
1023
- if (neg) {
1024
- *b = '-';
1025
- } else {
1026
- b++;
1027
- }
1057
+ b = oj_longlong_to_string(num, neg, b);
1028
1058
  } else {
1029
1059
  *b = '0';
1030
1060
  }
data/ext/oj/dump.h CHANGED
@@ -93,10 +93,7 @@ inline static void dump_ulong(unsigned long num, Out out) {
93
93
 
94
94
  *b-- = '\0';
95
95
  if (0 < num) {
96
- for (; 0 < num; num /= 10, b--) {
97
- *b = (num % 10) + '0';
98
- }
99
- b++;
96
+ b = oj_longlong_to_string((long long)num, false, b);
100
97
  } else {
101
98
  *b = '0';
102
99
  }
data/ext/oj/extconf.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mkmf'
2
4
  require 'rbconfig'
3
5
 
@@ -6,7 +8,7 @@ dir_config(extension_name)
6
8
 
7
9
  parts = RUBY_DESCRIPTION.split(' ')
8
10
  type = parts[0]
9
- type = type[4..-1] if type.start_with?('tcs-')
11
+ type = type[4..] if type.start_with?('tcs-')
10
12
  is_windows = RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/
11
13
  platform = RUBY_PLATFORM
12
14
  version = RUBY_VERSION.split('.')
@@ -56,7 +58,7 @@ dflags.each do |k, v|
56
58
  end
57
59
 
58
60
  $CPPFLAGS += ' -Wall'
59
- #puts "*** $CPPFLAGS: #{$CPPFLAGS}"
61
+ # puts "*** $CPPFLAGS: #{$CPPFLAGS}"
60
62
  # Adding the __attribute__ flag only works with gcc compilers and even then it
61
63
  # does not work to check args with varargs so just remove the check.
62
64
  CONFIG['warnflags'].slice!(/ -Wsuggest-attribute=format/)
data/ext/oj/fast.c CHANGED
@@ -40,7 +40,7 @@ typedef struct _doc {
40
40
  Leaf *where; // points to current location
41
41
  Leaf where_path[MAX_STACK]; // points to head of path
42
42
  char *json;
43
- unsigned long size; // number of leaves/branches in the doc
43
+ unsigned long size; // number of leaves/branches in the doc
44
44
  VALUE self;
45
45
  Batch batches;
46
46
  struct _batch batch0;
@@ -114,10 +114,7 @@ inline static char *ulong_fill(char *s, size_t num) {
114
114
  char *b = buf + sizeof(buf) - 1;
115
115
 
116
116
  *b-- = '\0';
117
- for (; 0 < num; num /= 10, b--) {
118
- *b = (num % 10) + '0';
119
- }
120
- b++;
117
+ b = oj_longlong_to_string((long long)num, false, b);
121
118
  if ('\0' == *b) {
122
119
  b--;
123
120
  *b = '0';
@@ -576,7 +573,7 @@ static char *read_quoted_value(ParseInfo pi) {
576
573
  char *h = pi->s; // head
577
574
  char *t = h; // tail
578
575
 
579
- h++; // skip quote character
576
+ h++; // skip quote character
580
577
  t++;
581
578
  value = h;
582
579
  for (; '"' != *h; h++, t++) {
data/ext/oj/mimic_json.c CHANGED
@@ -574,7 +574,6 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
574
574
  if (T_HASH != rb_type(ropts)) {
575
575
  rb_raise(rb_eArgError, "options must be a hash.");
576
576
  }
577
-
578
577
  rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
579
578
  v = rb_hash_lookup(ropts, oj_max_nesting_sym);
580
579
  if (Qtrue == v) {
@@ -738,6 +737,7 @@ static struct _options mimic_object_to_json_options = {0, // indent
738
737
  0, // array_size
739
738
  RaiseNan, // nan_dump
740
739
  false, // omit_nil
740
+ false, // omit_null_byte
741
741
  100, // max_depth
742
742
  },
743
743
  {
@@ -791,28 +791,48 @@ void oj_mimic_json_methods(VALUE json) {
791
791
  VALUE json_error;
792
792
  VALUE generator;
793
793
  VALUE ext;
794
+ VALUE verbose;
795
+
796
+ // rb_undef_method doesn't work for modules or maybe sometimes
797
+ // doesn't. Anyway setting verbose should hide the warning.
798
+ verbose = rb_gv_get("$VERBOSE");
799
+ rb_gv_set("$VERBOSE", Qfalse);
794
800
 
801
+ rb_undef_method(json, "create_id=");
795
802
  rb_define_module_function(json, "create_id=", mimic_set_create_id, 1);
803
+ rb_undef_method(json, "create_id");
796
804
  rb_define_module_function(json, "create_id", mimic_create_id, 0);
797
805
 
806
+ rb_undef_method(json, "dump");
798
807
  rb_define_module_function(json, "dump", mimic_dump, -1);
808
+ rb_undef_method(json, "load");
799
809
  rb_define_module_function(json, "load", mimic_load, -1);
800
810
  rb_define_module_function(json, "restore", mimic_load, -1);
811
+ rb_undef_method(json, "recurse_proc");
801
812
  rb_define_module_function(json, "recurse_proc", mimic_recurse_proc, 1);
813
+ rb_undef_method(json, "[]");
802
814
  rb_define_module_function(json, "[]", mimic_dump_load, -1);
803
815
 
816
+ rb_undef_method(json, "generate");
804
817
  rb_define_module_function(json, "generate", oj_mimic_generate, -1);
818
+ rb_undef_method(json, "fast_generate");
805
819
  rb_define_module_function(json, "fast_generate", oj_mimic_generate, -1);
820
+ rb_undef_method(json, "pretty_generate");
806
821
  rb_define_module_function(json, "pretty_generate", oj_mimic_pretty_generate, -1);
807
822
  // For older versions of JSON, the deprecated unparse methods.
823
+ rb_undef_method(json, "unparse");
808
824
  rb_define_module_function(json, "unparse", oj_mimic_generate, -1);
809
825
  rb_define_module_function(json, "fast_unparse", oj_mimic_generate, -1);
810
826
  rb_define_module_function(json, "pretty_unparse", oj_mimic_pretty_generate, -1);
811
827
 
828
+ rb_undef_method(json, "parse");
812
829
  rb_define_module_function(json, "parse", oj_mimic_parse, -1);
830
+ rb_undef_method(json, "parse!");
813
831
  rb_define_module_function(json, "parse!", mimic_parse_bang, -1);
814
832
 
833
+ rb_undef_method(json, "state");
815
834
  rb_define_module_function(json, "state", mimic_state, 0);
835
+ rb_gv_set("$VERBOSE", verbose);
816
836
 
817
837
  if (rb_const_defined_at(json, rb_intern("JSONError"))) {
818
838
  json_error = rb_const_get(json, rb_intern("JSONError"));
data/ext/oj/object.c CHANGED
@@ -446,9 +446,7 @@ WHICH_TYPE:
446
446
  rb_class2name(rb_obj_class(parent->val)));
447
447
  return;
448
448
  }
449
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
450
- oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
451
- }
449
+ TRACE_PARSE_CALL(pi->options.trace, "set_string", pi, rval);
452
450
  }
453
451
 
454
452
  static void hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
@@ -517,9 +515,7 @@ WHICH_TYPE:
517
515
  rb_class2name(rb_obj_class(parent->val)));
518
516
  return;
519
517
  }
520
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
521
- oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, rval);
522
- }
518
+ TRACE_PARSE_CALL(pi->options.trace, "add_number", pi, rval);
523
519
  }
524
520
 
525
521
  static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
@@ -603,9 +599,7 @@ WHICH_TYPE:
603
599
  rb_class2name(rb_obj_class(parent->val)));
604
600
  return;
605
601
  }
606
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
607
- oj_trace_parse_call("add_value", pi, __FILE__, __LINE__, value);
608
- }
602
+ TRACE_PARSE_CALL(pi->options.trace, "add_value", pi, value);
609
603
  }
610
604
 
611
605
  static VALUE start_hash(ParseInfo pi) {
@@ -651,32 +645,24 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
651
645
  }
652
646
  rval = str_to_value(pi, str, len, orig);
653
647
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
654
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
655
- oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rval);
656
- }
648
+ TRACE_PARSE_CALL(pi->options.trace, "append_string", pi, rval);
657
649
  }
658
650
 
659
651
  static void array_append_num(ParseInfo pi, NumInfo ni) {
660
652
  volatile VALUE rval = oj_num_as_value(ni);
661
653
 
662
654
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
663
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
664
- oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
665
- }
655
+ TRACE_PARSE_CALL(pi->options.trace, "append_number", pi, rval);
666
656
  }
667
657
 
668
658
  static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
669
659
  pi->stack.head->val = str_to_value(pi, str, len, orig);
670
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
671
- oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, pi->stack.head->val);
672
- }
660
+ TRACE_PARSE_CALL(pi->options.trace, "add_string", pi, pi->stack.head->val);
673
661
  }
674
662
 
675
663
  static void add_num(ParseInfo pi, NumInfo ni) {
676
664
  pi->stack.head->val = oj_num_as_value(ni);
677
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
678
- oj_trace_parse_call("add_num", pi, __FILE__, __LINE__, pi->stack.head->val);
679
- }
665
+ TRACE_PARSE_CALL(pi->options.trace, "add_num", pi, pi->stack.head->val);
680
666
  }
681
667
 
682
668
  void oj_set_object_callbacks(ParseInfo pi) {
data/ext/oj/oj.c CHANGED
@@ -135,6 +135,7 @@ static VALUE newline_sym;
135
135
  static VALUE nilnil_sym;
136
136
  static VALUE null_sym;
137
137
  static VALUE object_sym;
138
+ static VALUE omit_null_byte_sym;
138
139
  static VALUE omit_nil_sym;
139
140
  static VALUE rails_sym;
140
141
  static VALUE raise_sym;
@@ -222,6 +223,7 @@ struct _options oj_default_options = {
222
223
  0, // array_size
223
224
  AutoNan, // nan_dump
224
225
  false, // omit_nil
226
+ false, // omit_null_byte
225
227
  MAX_DEPTH, // max_depth
226
228
  },
227
229
  {
@@ -230,7 +232,7 @@ struct _options oj_default_options = {
230
232
  NULL, // tail
231
233
  {'\0'}, // err
232
234
  },
233
- NULL, // ignore
235
+ NULL, // ignore
234
236
  };
235
237
 
236
238
  /* Document-method: default_options()
@@ -291,6 +293,7 @@ struct _options oj_default_options = {
291
293
  *used
292
294
  * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load
293
295
  * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted
296
+ * - *:omit_null_byte* [_true_|_false_] if true null bytes in strings will be omitted when dumping
294
297
  * - *:ignore* [_nil_|_Array_] either nil or an Array of classes to ignore when dumping
295
298
  * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when dumping in
296
299
  *object or custom mode.
@@ -384,6 +387,7 @@ static VALUE get_def_opts(VALUE self) {
384
387
  opts,
385
388
  cache_keys_sym,
386
389
  (Yes == oj_default_options.cache_keys) ? Qtrue : ((No == oj_default_options.cache_keys) ? Qfalse : Qnil));
390
+
387
391
  switch (oj_default_options.mode) {
388
392
  case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
389
393
  case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
@@ -459,6 +463,7 @@ static VALUE get_def_opts(VALUE self) {
459
463
  default: rb_hash_aset(opts, nan_sym, auto_sym); break;
460
464
  }
461
465
  rb_hash_aset(opts, omit_nil_sym, oj_default_options.dump_opts.omit_nil ? Qtrue : Qfalse);
466
+ rb_hash_aset(opts, omit_null_byte_sym, oj_default_options.dump_opts.omit_null_byte ? Qtrue : Qfalse);
462
467
  rb_hash_aset(opts, oj_hash_class_sym, oj_default_options.hash_class);
463
468
  rb_hash_aset(opts, oj_array_class_sym, oj_default_options.array_class);
464
469
 
@@ -865,6 +870,17 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
865
870
  } else {
866
871
  rb_raise(rb_eArgError, ":omit_nil must be true or false.");
867
872
  }
873
+ } else if (omit_null_byte_sym == k) {
874
+ if (Qnil == v) {
875
+ return ST_CONTINUE;
876
+ }
877
+ if (Qtrue == v) {
878
+ copts->dump_opts.omit_null_byte = true;
879
+ } else if (Qfalse == v) {
880
+ copts->dump_opts.omit_null_byte = false;
881
+ } else {
882
+ rb_raise(rb_eArgError, ":omit_null_byte must be true or false.");
883
+ }
868
884
  } else if (oj_ascii_only_sym == k) {
869
885
  // This is here only for backwards compatibility with the original Oj.
870
886
  if (Qtrue == v) {
@@ -1278,8 +1294,9 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1278
1294
 
1279
1295
  oj_out_init(arg.out);
1280
1296
 
1281
- arg.out->omit_nil = copts.dump_opts.omit_nil;
1282
- arg.out->caller = CALLER_DUMP;
1297
+ arg.out->omit_nil = copts.dump_opts.omit_nil;
1298
+ arg.out->omit_null_byte = copts.dump_opts.omit_null_byte;
1299
+ arg.out->caller = CALLER_DUMP;
1283
1300
 
1284
1301
  return rb_ensure(dump_body, (VALUE)&arg, dump_ensure, (VALUE)&arg);
1285
1302
  }
@@ -1326,7 +1343,8 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1326
1343
 
1327
1344
  oj_out_init(&out);
1328
1345
 
1329
- out.omit_nil = copts.dump_opts.omit_nil;
1346
+ out.omit_nil = copts.dump_opts.omit_nil;
1347
+ out.omit_null_byte = copts.dump_opts.omit_null_byte;
1330
1348
  // For obj.to_json or generate nan is not allowed but if called from dump
1331
1349
  // it is.
1332
1350
  oj_dump_obj_to_json_using_params(*argv, &copts, &out, argc - 1, argv + 1);
@@ -1967,6 +1985,8 @@ void Init_oj(void) {
1967
1985
  rb_gc_register_address(&oj_quirks_mode_sym);
1968
1986
  oj_safe_sym = ID2SYM(rb_intern("safe"));
1969
1987
  rb_gc_register_address(&oj_safe_sym);
1988
+ omit_null_byte_sym = ID2SYM(rb_intern("omit_null_byte"));
1989
+ rb_gc_register_address(&omit_null_byte_sym);
1970
1990
  oj_space_before_sym = ID2SYM(rb_intern("space_before"));
1971
1991
  rb_gc_register_address(&oj_space_before_sym);
1972
1992
  oj_space_sym = ID2SYM(rb_intern("space"));
data/ext/oj/oj.h CHANGED
@@ -124,6 +124,7 @@ typedef struct _dumpOpts {
124
124
  uint8_t array_size;
125
125
  char nan_dump; // NanDump
126
126
  bool omit_nil;
127
+ bool omit_null_byte;
127
128
  int max_depth;
128
129
  } *DumpOpts;
129
130
 
@@ -199,6 +200,7 @@ typedef struct _out {
199
200
  uint32_t hash_cnt;
200
201
  bool allocated;
201
202
  bool omit_nil;
203
+ bool omit_null_byte;
202
204
  int argc;
203
205
  VALUE *argv;
204
206
  DumpCaller caller; // used for the mimic json only
@@ -260,12 +262,13 @@ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
260
262
  extern bool oj_hash_has_key(VALUE hash, VALUE key);
261
263
  extern void oj_parse_options(VALUE ropts, Options copts);
262
264
 
263
- extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
264
- extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
265
- extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
266
- extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
267
- extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
268
- extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
265
+ extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
266
+ extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
267
+ extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
268
+ extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
269
+ extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
270
+ extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
271
+ extern char *oj_longlong_to_string(long long num, bool negative, char *buf);
269
272
 
270
273
  extern void oj_str_writer_push_key(StrWriter sw, const char *key);
271
274
  extern void oj_str_writer_push_object(StrWriter sw, const char *key);
@@ -323,6 +326,7 @@ extern VALUE oj_max_nesting_sym;
323
326
  extern VALUE oj_object_class_sym;
324
327
  extern VALUE oj_object_nl_sym;
325
328
  extern VALUE oj_quirks_mode_sym;
329
+ extern VALUE oj_skip_null_byte_sym;
326
330
  extern VALUE oj_space_before_sym;
327
331
  extern VALUE oj_space_sym;
328
332
  extern VALUE oj_symbolize_names_sym;
data/ext/oj/parse.c CHANGED
@@ -426,6 +426,7 @@ static void read_num(ParseInfo pi) {
426
426
  struct _numInfo ni;
427
427
  Val parent = stack_peek(&pi->stack);
428
428
 
429
+ ni.pi = pi;
429
430
  ni.str = pi->cur;
430
431
  ni.i = 0;
431
432
  ni.num = 0;
@@ -709,10 +710,7 @@ void oj_parse2(ParseInfo pi) {
709
710
  case '[': array_start(pi); break;
710
711
  case ']': array_end(pi); break;
711
712
  case ',': comma(pi); break;
712
- case '"':
713
- read_str(pi);
714
- break;
715
- // case '+':
713
+ case '"': read_str(pi); break;
716
714
  case '+':
717
715
  if (CompatMode == pi->options.mode) {
718
716
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
@@ -877,7 +875,7 @@ oj_num_as_value(NumInfo ni) {
877
875
  double d = strtod(ni->str, &end);
878
876
 
879
877
  if ((long)ni->len != (long)(end - ni->str)) {
880
- rb_raise(oj_parse_error_class, "Invalid float");
878
+ rb_raise(ni->pi->err_class, "Invalid float");
881
879
  }
882
880
  rnum = rb_float_new(d);
883
881
  }