oj 3.14.3 → 3.15.1

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