oj 3.16.3 β†’ 3.16.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a6b15904386e7010fa071ecc27b8f3a6d5023c8162ea7042fc39e29c373a20a
4
- data.tar.gz: b862e59cff0ba3a15eadec34fe807d5893c9c1d1e9fcce01ced66ab28e642181
3
+ metadata.gz: bd443226b6fbe558bd4a01e5e4e2c150149e697c22c1676007f67923b9832682
4
+ data.tar.gz: 0a46ac536c4fccaeac2bddde9ff8ef8d50343f5eeff3455b36c5bfb762aef073
5
5
  SHA512:
6
- metadata.gz: f186fb40ebcc3792b4949f81ce3b2efb22bee9dce9b44785eeca37cc27e25b79e8afa4fc5872232001d28a3d0b279fc222d5f7d2f8aaa091fe3a135246139b64
7
- data.tar.gz: 9c1536bfc15eb3fe02e92d803417ef1ccce5084036aaf8df5ae2b61995a429ab8ae00be5e9b7c79e11839d781c67523ed5bc534df36a40b59dcc61db6551158f
6
+ metadata.gz: 49e28b9151a1c84d9f97adcea6af90acd1141a194858f29e633bf5e2e1965bb5fab783c015e343f569692e6351ad3957059135f05eb50022b79ee799ae0a306a
7
+ data.tar.gz: 74f69e8e14c39aee7128f8b7dc23fa1fa60a8df383dec844257e67c6e7f34c58d430ac77bc06571e8d52a29f857ee0162cba8ad1e93ac8bc1f7c2ad0260a0002
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.16.4 - 2024-06-08
4
+
5
+ - Fixed Oj::Parse EOF issue on larger stream input.
6
+
3
7
  ## 3.16.3 - 2023-12-11
4
8
 
5
9
  - Fixed the gemspec to allow earlier versions of the bigdecimal gem.
data/ext/oj/dump.c CHANGED
@@ -198,7 +198,18 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
198
198
  }
199
199
 
200
200
  inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
201
- return calculate_string_size(str, len, rails_friendly_chars);
201
+ long size = 0;
202
+ size_t i = len;
203
+ uint8_t hi = 0;
204
+
205
+ for (; 0 < i; str++, i--) {
206
+ size += rails_friendly_chars[*str];
207
+ hi |= *str & 0x80;
208
+ }
209
+ if (0 == hi) {
210
+ return size - len * (size_t)'0';
211
+ }
212
+ return -(size - len * (size_t)'0');
202
213
  }
203
214
 
204
215
  const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
@@ -750,8 +761,9 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
750
761
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
751
762
  size_t size;
752
763
  char *cmap;
753
- const char *orig = str;
754
- bool has_hi = false;
764
+ const char *orig = str;
765
+ bool has_hi = false;
766
+ bool do_unicode_validation = false;
755
767
 
756
768
  switch (out->opts->escape_mode) {
757
769
  case NLEsc:
@@ -772,8 +784,9 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
772
784
  size = xss_friendly_size((uint8_t *)str, cnt);
773
785
  break;
774
786
  case JXEsc:
775
- cmap = hixss_friendly_chars;
776
- size = hixss_friendly_size((uint8_t *)str, cnt);
787
+ cmap = hixss_friendly_chars;
788
+ size = hixss_friendly_size((uint8_t *)str, cnt);
789
+ do_unicode_validation = true;
777
790
  break;
778
791
  case RailsXEsc: {
779
792
  long sz;
@@ -786,12 +799,22 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
786
799
  } else {
787
800
  size = (size_t)sz;
788
801
  }
802
+ do_unicode_validation = true;
789
803
  break;
790
804
  }
791
- case RailsEsc:
805
+ case RailsEsc: {
806
+ long sz;
792
807
  cmap = rails_friendly_chars;
793
- size = rails_friendly_size((uint8_t *)str, cnt);
808
+ sz = rails_friendly_size((uint8_t *)str, cnt);
809
+ if (sz < 0) {
810
+ has_hi = true;
811
+ size = (size_t)-sz;
812
+ } else {
813
+ size = (size_t)sz;
814
+ }
815
+ do_unicode_validation = true;
794
816
  break;
817
+ }
795
818
  case JSONEsc:
796
819
  default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
797
820
  }
@@ -822,7 +845,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
822
845
  for (; str < end; str++) {
823
846
  switch (cmap[(uint8_t)*str]) {
824
847
  case '1':
825
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && check_start <= str) {
848
+ if (do_unicode_validation && check_start <= str) {
826
849
  if (0 != (0x80 & (uint8_t)*str)) {
827
850
  if (0xC0 == (0xC0 & (uint8_t)*str)) {
828
851
  check_start = check_unicode(str, end, orig);
@@ -846,8 +869,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
846
869
  }
847
870
  break;
848
871
  case '3': // Unicode
849
- if (0xe2 == (uint8_t)*str && (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) &&
850
- 2 <= end - str) {
872
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
851
873
  if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
852
874
  str = dump_unicode(str, end, out, orig);
853
875
  } else {
@@ -866,8 +888,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
866
888
  APPEND_CHARS(out->cur, "\\u00", 4);
867
889
  dump_hex((uint8_t)*str, out);
868
890
  } else {
869
- if (0xe2 == (uint8_t)*str &&
870
- (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
891
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
871
892
  if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
872
893
  str = dump_unicode(str, end, out, orig);
873
894
  } else {
@@ -884,8 +905,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
884
905
  }
885
906
  *out->cur++ = '"';
886
907
  }
887
- if ((JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 0 < str - orig &&
888
- 0 != (0x80 & *(str - 1))) {
908
+ if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
889
909
  uint8_t c = (uint8_t) * (str - 1);
890
910
  int i;
891
911
  int scnt = (int)(str - orig);
data/ext/oj/extconf.rb CHANGED
@@ -29,10 +29,9 @@ dflags = {
29
29
  have_func('rb_gc_mark_movable')
30
30
  have_func('stpcpy')
31
31
  have_func('pthread_mutex_init')
32
+ have_func('getrlimit', 'sys/resource.h')
32
33
  have_func('rb_enc_interned_str')
33
34
  have_func('rb_ext_ractor_safe', 'ruby.h')
34
- # rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
35
- have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == version[1]
36
35
 
37
36
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
38
37
 
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;
@@ -573,7 +573,7 @@ static char *read_quoted_value(ParseInfo pi) {
573
573
  char *h = pi->s; // head
574
574
  char *t = h; // tail
575
575
 
576
- h++; // skip quote character
576
+ h++; // skip quote character
577
577
  t++;
578
578
  value = h;
579
579
  for (; '"' != *h; h++, t++) {
@@ -765,7 +765,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
765
765
  pi.s = pi.str;
766
766
  doc_init(doc);
767
767
  pi.doc = doc;
768
- #if IS_WINDOWS
768
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
769
769
  // assume a 1M stack and give half to ruby
770
770
  pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
771
771
  #else
data/ext/oj/oj.c CHANGED
@@ -36,6 +36,7 @@ ID oj_as_json_id;
36
36
  ID oj_begin_id;
37
37
  ID oj_bigdecimal_id;
38
38
  ID oj_end_id;
39
+ ID oj_eofq_id;
39
40
  ID oj_exclude_end_id;
40
41
  ID oj_error_id;
41
42
  ID oj_file_id;
@@ -1849,6 +1850,7 @@ void Init_oj(void) {
1849
1850
  oj_begin_id = rb_intern("begin");
1850
1851
  oj_bigdecimal_id = rb_intern("BigDecimal");
1851
1852
  oj_end_id = rb_intern("end");
1853
+ oj_eofq_id = rb_intern("eof?");
1852
1854
  oj_error_id = rb_intern("error");
1853
1855
  oj_exclude_end_id = rb_intern("exclude_end?");
1854
1856
  oj_file_id = rb_intern("file?");
data/ext/oj/oj.h CHANGED
@@ -334,6 +334,7 @@ extern ID oj_as_json_id;
334
334
  extern ID oj_begin_id;
335
335
  extern ID oj_bigdecimal_id;
336
336
  extern ID oj_end_id;
337
+ extern ID oj_eofq_id;
337
338
  extern ID oj_error_id;
338
339
  extern ID oj_exclude_end_id;
339
340
  extern ID oj_file_id;
data/ext/oj/parser.c CHANGED
@@ -1114,9 +1114,6 @@ static void parse(ojParser p, const byte *json) {
1114
1114
  p->map = trail_map;
1115
1115
  }
1116
1116
  }
1117
- if (0 < p->depth) {
1118
- parse_error(p, "parse error, not closed");
1119
- }
1120
1117
  if (0 == p->depth) {
1121
1118
  switch (p->map[256]) {
1122
1119
  case '0':
@@ -1394,6 +1391,12 @@ static VALUE load(VALUE self) {
1394
1391
  if (0 < RSTRING_LEN(rbuf)) {
1395
1392
  parse(p, (byte *)StringValuePtr(rbuf));
1396
1393
  }
1394
+ if (Qtrue == rb_funcall(p->reader, oj_eofq_id, 0)) {
1395
+ if (0 < p->depth) {
1396
+ parse_error(p, "parse error, not closed");
1397
+ }
1398
+ break;
1399
+ }
1397
1400
  }
1398
1401
  return Qtrue;
1399
1402
  }
data/ext/oj/parser.h CHANGED
@@ -32,9 +32,9 @@ typedef struct _num {
32
32
  long double dub;
33
33
  int64_t fixnum; // holds all digits
34
34
  uint32_t len;
35
- int16_t div; // 10^div
35
+ int16_t div; // 10^div
36
36
  int16_t exp;
37
- uint8_t shift; // shift of fixnum to get decimal
37
+ uint8_t shift; // shift of fixnum to get decimal
38
38
  bool neg;
39
39
  bool exp_neg;
40
40
  // for numbers as strings, reuse buf
data/ext/oj/reader.c CHANGED
@@ -101,7 +101,7 @@ int oj_reader_read(Reader reader) {
101
101
  } else {
102
102
  shift = reader->pro - reader->head - 1; // leave one character so we can backup one
103
103
  }
104
- if (0 >= shift) { /* no space left so allocate more */
104
+ if (0 >= shift) { /* no space left so allocate more */
105
105
  const char *old = reader->head;
106
106
  size_t size = reader->end - reader->head + BUF_PAD;
107
107
 
data/ext/oj/saj.c CHANGED
@@ -578,7 +578,7 @@ static void saj_parse(VALUE handler, char *json) {
578
578
  /* initialize parse info */
579
579
  pi.str = json;
580
580
  pi.s = json;
581
- #if IS_WINDOWS
581
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
582
582
  pi.stack_min = (void *)((char *)&obj - (512L * 1024L)); /* assume a 1M stack and give half to ruby */
583
583
  #else
584
584
  {
@@ -587,7 +587,7 @@ static void saj_parse(VALUE handler, char *json) {
587
587
  if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
588
588
  pi.stack_min = (void *)((char *)&obj - (lim.rlim_cur / 4 * 3)); /* let 3/4ths of the stack be used only */
589
589
  } else {
590
- pi.stack_min = 0; /* indicates not to check stack limit */
590
+ pi.stack_min = 0; /* indicates not to check stack limit */
591
591
  }
592
592
  }
593
593
  #endif
data/ext/oj/usual.c CHANGED
@@ -281,7 +281,6 @@ static void close_object(ojParser p) {
281
281
  VALUE *head = d->vhead + c->vi + 1;
282
282
  volatile VALUE obj = rb_hash_new();
283
283
 
284
- #if HAVE_RB_HASH_BULK_INSERT
285
284
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
286
285
  *vp = d->get_key(p, kp);
287
286
  if (sizeof(kp->buf) <= (size_t)kp->len) {
@@ -289,14 +288,6 @@ static void close_object(ojParser p) {
289
288
  }
290
289
  }
291
290
  rb_hash_bulk_insert(d->vtail - head, head, obj);
292
- #else
293
- for (vp = head; kp < d->ktail; kp++, vp += 2) {
294
- rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
295
- if (sizeof(kp->buf) <= (size_t)kp->len) {
296
- OJ_R_FREE(kp->key);
297
- }
298
- }
299
- #endif
300
291
  d->ktail = d->khead + c->ki;
301
292
  d->vtail = head;
302
293
  head--;
@@ -341,7 +332,6 @@ static void close_object_create(ojParser p) {
341
332
  head++;
342
333
  if (Qnil == d->hash_class) {
343
334
  obj = rb_hash_new();
344
- #if HAVE_RB_HASH_BULK_INSERT
345
335
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
346
336
  *vp = d->get_key(p, kp);
347
337
  if (sizeof(kp->buf) <= (size_t)kp->len) {
@@ -349,14 +339,6 @@ static void close_object_create(ojParser p) {
349
339
  }
350
340
  }
351
341
  rb_hash_bulk_insert(d->vtail - head, head, obj);
352
- #else
353
- for (vp = head; kp < d->ktail; kp++, vp += 2) {
354
- rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
355
- if (sizeof(kp->buf) <= (size_t)kp->len) {
356
- OJ_R_FREE(kp->key);
357
- }
358
- }
359
- #endif
360
342
  } else {
361
343
  obj = rb_class_new_instance(0, NULL, d->hash_class);
362
344
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
@@ -373,7 +355,6 @@ static void close_object_create(ojParser p) {
373
355
  if (!d->ignore_json_create && rb_respond_to(clas, oj_json_create_id)) {
374
356
  volatile VALUE arg = rb_hash_new();
375
357
 
376
- #if HAVE_RB_HASH_BULK_INSERT
377
358
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
378
359
  *vp = d->get_key(p, kp);
379
360
  if (sizeof(kp->buf) <= (size_t)kp->len) {
@@ -381,14 +362,6 @@ static void close_object_create(ojParser p) {
381
362
  }
382
363
  }
383
364
  rb_hash_bulk_insert(d->vtail - head, head, arg);
384
- #else
385
- for (vp = head; kp < d->ktail; kp++, vp += 2) {
386
- rb_hash_aset(arg, d->get_key(p, kp), *(vp + 1));
387
- if (sizeof(kp->buf) <= (size_t)kp->len) {
388
- OJ_R_FREE(kp->key);
389
- }
390
- }
391
- #endif
392
365
  obj = rb_funcall(clas, oj_json_create_id, 1, arg);
393
366
  } else {
394
367
  obj = rb_class_new_instance(0, NULL, clas);
data/lib/oj/schandler.rb CHANGED
@@ -64,13 +64,14 @@ module Oj
64
64
  #
65
65
  # hash_end
66
66
  #
67
- # When a hash key is encountered the hash_key method is called with the parsed
68
- # hash value key. The return value from the call is then used as the key in
69
- # the key-value pair that follows.
67
+ # At the end of a JSON object element the hash_end() callback is called if
68
+ # public.
70
69
  #
71
70
  # hash_key
72
71
  #
73
- # At the end of a JSON object element the hash_end() callback is called if public.
72
+ # When a hash key is encountered the hash_key() method is called with the
73
+ # parsed hash value key. The return value from the call is then used as the
74
+ # key in the key-value pair that follows.
74
75
  #
75
76
  # hash_set
76
77
  #
data/lib/oj/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Oj
2
2
  # Current version of the module.
3
- VERSION = '3.16.3'
3
+ VERSION = '3.16.4'
4
4
  end
@@ -74,42 +74,77 @@ class TestJSONEncoding < ActiveSupport::TestCase
74
74
  ActiveSupport.escape_html_entities_in_json = false
75
75
  end
76
76
 
77
- def test_utf8_string_encoded_properly
78
- # The original test seems to expect that
79
- # ActiveSupport.escape_html_entities_in_json reverts to true even after
80
- # being set to false. I haven't been able to figure that out so the value is
81
- # set to true, the default, before running the test. This might be wrong but
82
- # for now it will have to do.
83
- ActiveSupport.escape_html_entities_in_json = true
84
- result = ActiveSupport::JSON.encode("€2.99")
85
- assert_equal '"€2.99"', result
86
- assert_equal(Encoding::UTF_8, result.encoding)
87
-
88
- result = ActiveSupport::JSON.encode("✎☺")
89
- assert_equal '"✎☺"', result
90
- assert_equal(Encoding::UTF_8, result.encoding)
77
+ def test_hash_keys_encoding_without_escaping
78
+ assert_equal "{\"<>\":\"<>\"}", ActiveSupport::JSON.encode("<>" => "<>")
91
79
  end
92
80
 
93
- def test_non_utf8_string_transcodes
94
- s = "二".encode("Shift_JIS")
95
- result = ActiveSupport::JSON.encode(s)
96
- assert_equal '"二"', result
97
- assert_equal Encoding::UTF_8, result.encoding
81
+ module UnicodeTests
82
+ def test_utf8_string_encoded_properly
83
+ result = ActiveSupport::JSON.encode("€2.99")
84
+ assert_equal '"€2.99"', result
85
+ assert_equal(Encoding::UTF_8, result.encoding)
86
+
87
+ result = ActiveSupport::JSON.encode("✎☺")
88
+ assert_equal '"✎☺"', result
89
+ assert_equal(Encoding::UTF_8, result.encoding)
90
+ end
91
+
92
+ def test_non_utf8_string_transcodes
93
+ s = "二".encode("Shift_JIS")
94
+ result = ActiveSupport::JSON.encode(s)
95
+ assert_equal '"二"', result
96
+ assert_equal Encoding::UTF_8, result.encoding
97
+ end
98
+
99
+ def test_wide_utf8_chars
100
+ w = "𠜎"
101
+ result = ActiveSupport::JSON.encode(w)
102
+ assert_equal '"𠜎"', result
103
+ end
104
+
105
+ def test_wide_utf8_roundtrip
106
+ hash = { string: "𐒑" }
107
+ json = ActiveSupport::JSON.encode(hash)
108
+ decoded_hash = ActiveSupport::JSON.decode(json)
109
+ assert_equal "𐒑", decoded_hash["string"]
110
+ end
111
+
112
+ def test_invalid_encoding_raises
113
+ s = "\xAE\xFF\x9F"
114
+ refute s.valid_encoding?
115
+
116
+ # n.b. this raises EncodingError, because we didn't call Oj.mimic_JSON in the test setup; but,
117
+ # if you do that (even indirectly through Oj.optimize_rails), then this raises a
118
+ # JSON::GeneratorError instead of an EncodingError.
119
+ assert_raises(EncodingError) do
120
+ ActiveSupport::JSON.encode([s])
121
+ end
122
+ end
98
123
  end
99
124
 
100
- def test_wide_utf8_chars
101
- w = "𠜎"
102
- result = ActiveSupport::JSON.encode(w)
103
- assert_equal '"𠜎"', result
125
+ module UnicodeTestsWithEscapingOn
126
+ def setup
127
+ ActiveSupport.escape_html_entities_in_json = true
128
+ end
129
+
130
+ def teardown
131
+ ActiveSupport.escape_html_entities_in_json = false
132
+ end
133
+
134
+ include UnicodeTests
104
135
  end
105
136
 
106
- def test_wide_utf8_roundtrip
107
- hash = { string: "𐒑" }
108
- json = ActiveSupport::JSON.encode(hash)
109
- decoded_hash = ActiveSupport::JSON.decode(json)
110
- assert_equal "𐒑", decoded_hash["string"]
137
+ module UnicodeTestsWithEscapingOff
138
+ def setup
139
+ ActiveSupport.escape_html_entities_in_json = false
140
+ end
141
+
142
+ include UnicodeTests
111
143
  end
112
144
 
145
+ include UnicodeTestsWithEscapingOn
146
+ include UnicodeTestsWithEscapingOff
147
+
113
148
  def test_hash_key_identifiers_are_always_quoted
114
149
  values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
115
150
  assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
@@ -8,9 +8,18 @@ require "active_support/time"
8
8
  require_relative "time_zone_test_helpers"
9
9
  require_relative "encoding_test_cases"
10
10
 
11
+ require 'oj'
12
+ # Sets the ActiveSupport encoder to be Oj and also wraps the setting of globals.
13
+ Oj::Rails.set_encoder()
14
+ Oj::Rails.optimize()
15
+
11
16
  class TestJSONEncoding < ActiveSupport::TestCase
12
17
  include TimeZoneTestHelpers
13
18
 
19
+ def test_is_actually_oj
20
+ assert_equal Oj::Rails::Encoder, ActiveSupport.json_encoder
21
+ end
22
+
14
23
  def sorted_json(json)
15
24
  if json.start_with?("{") && json.end_with?("}")
16
25
  "{" + json[1..-2].split(",").sort.join(",") + "}"
@@ -61,36 +70,77 @@ class TestJSONEncoding < ActiveSupport::TestCase
61
70
  ActiveSupport.escape_html_entities_in_json = false
62
71
  end
63
72
 
64
- def test_utf8_string_encoded_properly
65
- result = ActiveSupport::JSON.encode("€2.99")
66
- assert_equal '"€2.99"', result
67
- assert_equal(Encoding::UTF_8, result.encoding)
68
-
69
- result = ActiveSupport::JSON.encode("✎☺")
70
- assert_equal '"✎☺"', result
71
- assert_equal(Encoding::UTF_8, result.encoding)
73
+ def test_hash_keys_encoding_without_escaping
74
+ assert_equal "{\"<>\":\"<>\"}", ActiveSupport::JSON.encode("<>" => "<>")
72
75
  end
73
76
 
74
- def test_non_utf8_string_transcodes
75
- s = "二".encode("Shift_JIS")
76
- result = ActiveSupport::JSON.encode(s)
77
- assert_equal '"二"', result
78
- assert_equal Encoding::UTF_8, result.encoding
77
+ module UnicodeTests
78
+ def test_utf8_string_encoded_properly
79
+ result = ActiveSupport::JSON.encode("€2.99")
80
+ assert_equal '"€2.99"', result
81
+ assert_equal(Encoding::UTF_8, result.encoding)
82
+
83
+ result = ActiveSupport::JSON.encode("✎☺")
84
+ assert_equal '"✎☺"', result
85
+ assert_equal(Encoding::UTF_8, result.encoding)
86
+ end
87
+
88
+ def test_non_utf8_string_transcodes
89
+ s = "二".encode("Shift_JIS")
90
+ result = ActiveSupport::JSON.encode(s)
91
+ assert_equal '"二"', result
92
+ assert_equal Encoding::UTF_8, result.encoding
93
+ end
94
+
95
+ def test_wide_utf8_chars
96
+ w = "𠜎"
97
+ result = ActiveSupport::JSON.encode(w)
98
+ assert_equal '"𠜎"', result
99
+ end
100
+
101
+ def test_wide_utf8_roundtrip
102
+ hash = { string: "𐒑" }
103
+ json = ActiveSupport::JSON.encode(hash)
104
+ decoded_hash = ActiveSupport::JSON.decode(json)
105
+ assert_equal "𐒑", decoded_hash["string"]
106
+ end
107
+
108
+ def test_invalid_encoding_raises
109
+ s = "\xAE\xFF\x9F"
110
+ refute s.valid_encoding?
111
+
112
+ # n.b. this raises EncodingError, because we didn't call Oj.mimic_JSON in the test setup; but,
113
+ # if you do that (even indirectly through Oj.optimize_rails), then this raises a
114
+ # JSON::GeneratorError instead of an EncodingError.
115
+ assert_raises(EncodingError) do
116
+ ActiveSupport::JSON.encode([s])
117
+ end
118
+ end
79
119
  end
80
120
 
81
- def test_wide_utf8_chars
82
- w = "𠜎"
83
- result = ActiveSupport::JSON.encode(w)
84
- assert_equal '"𠜎"', result
121
+ module UnicodeTestsWithEscapingOn
122
+ def setup
123
+ ActiveSupport.escape_html_entities_in_json = true
124
+ end
125
+
126
+ def teardown
127
+ ActiveSupport.escape_html_entities_in_json = false
128
+ end
129
+
130
+ include UnicodeTests
85
131
  end
86
132
 
87
- def test_wide_utf8_roundtrip
88
- hash = { string: "𐒑" }
89
- json = ActiveSupport::JSON.encode(hash)
90
- decoded_hash = ActiveSupport::JSON.decode(json)
91
- assert_equal "𐒑", decoded_hash["string"]
133
+ module UnicodeTestsWithEscapingOff
134
+ def setup
135
+ ActiveSupport.escape_html_entities_in_json = false
136
+ end
137
+
138
+ include UnicodeTests
92
139
  end
93
140
 
141
+ include UnicodeTestsWithEscapingOn
142
+ include UnicodeTestsWithEscapingOff
143
+
94
144
  def test_hash_key_identifiers_are_always_quoted
95
145
  values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
96
146
  assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.16.3
4
+ version: 3.16.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-11 00:00:00.000000000 Z
11
+ date: 2024-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal