oj 3.13.16 → 3.13.17

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d07b4081b5dda88146594adba591542d11106f1c1eadcf7c56e38358d0ee0af3
4
- data.tar.gz: 6bdc82c3812f45a8ae9d72ec0b2f43ee223d95ac250399fc8dae8daf34cd3b49
3
+ metadata.gz: b2ba7c6f3821ac59250c2893d01269cd45bb2ec5eb4b06babccf480a07fb8862
4
+ data.tar.gz: 666b047d4ac8f5637e6b1e8ca995bceb105197eabc4664936cc72986280684c2
5
5
  SHA512:
6
- metadata.gz: 4358aa15b7956a5b577163e43fbdc0512212a218124878ceedc2932483f27acabed2d2b92eb888c8e54601f4f4aaf3dce3b959a36effbea989e7e7ea8956d1c1
7
- data.tar.gz: 1e1710bce62aadfbcc1196357d2e692892b75cc18c982fb6f53c2e35782d9bd2256cb3b6ab98951691430f23e8c2d8db26987a95dcba29f87b472729613b8d2d
6
+ metadata.gz: 85005fe527745233220a680adf9bf3c24bb711abbe6b6758ccff236398c7f1218c9bc586eea7954388548772b17073e5540595d160948e19fffb94e535363ea4
7
+ data.tar.gz: 5b8b227c2d7e8156adb1adccff99ca18a8f2735ec3b7af9f8ee3f2b747427459c497ae738fe6c941d3df6e5131342677b1e8c08f208edeb7988697f80499339d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.13.17 - 2022-07-15
4
+
5
+ - Fixed Oj::Parser to detect unterminated arrays and objects.
6
+
3
7
  ## 3.13.16 - 2022-07-06
4
8
 
5
9
  - Added line and column as optional arguments to the Oj::Parser.saj parser.
data/ext/oj/buf.h CHANGED
@@ -39,6 +39,10 @@ inline static const char *buf_str(Buf buf) {
39
39
  }
40
40
 
41
41
  inline static void buf_append_string(Buf buf, const char *s, size_t slen) {
42
+ if (0 == slen) {
43
+ return;
44
+ }
45
+
42
46
  if (buf->end <= buf->tail + slen) {
43
47
  size_t len = buf->end - buf->head;
44
48
  size_t toff = buf->tail - buf->head;
data/ext/oj/parse.c CHANGED
@@ -183,6 +183,36 @@ static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
183
183
  }
184
184
  }
185
185
 
186
+ static inline const char *scan_string_noSIMD(const char *str, const char *end) {
187
+ for (; '"' != *str; str++) {
188
+ if (end <= str || '\0' == *str || '\\' == *str) {
189
+ break;
190
+ }
191
+ }
192
+ return str;
193
+ }
194
+
195
+ #if defined(OJ_USE_SSE4_2)
196
+ #include <nmmintrin.h>
197
+
198
+ static inline const char *scan_string_SIMD(const char *str, const char *end) {
199
+ static const char chars[16] = "\x00\\\"";
200
+ const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
201
+ const char *_end = (const char *)(end - 16);
202
+
203
+ for (; str <= _end; str += 16) {
204
+ const __m128i string = _mm_loadu_si128((const __m128i *)str);
205
+ const int r = _mm_cmpestri(terminate, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
206
+ if (r != 16) {
207
+ str = (char*)(str + r);
208
+ return str;
209
+ }
210
+ }
211
+
212
+ return scan_string_noSIMD(str, end);
213
+ }
214
+ #endif
215
+
186
216
  // entered at /
187
217
  static void read_escaped_str(ParseInfo pi, const char *start) {
188
218
  struct _buf buf;
@@ -192,11 +222,15 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
192
222
  Val parent = stack_peek(&pi->stack);
193
223
 
194
224
  buf_init(&buf);
195
- if (0 < cnt) {
196
- buf_append_string(&buf, start, cnt);
197
- }
198
- for (s = pi->cur; '"' != *s; s++) {
199
- if (s >= pi->end) {
225
+ buf_append_string(&buf, start, cnt);
226
+
227
+ for (s = pi->cur; '"' != *s;) {
228
+ #if defined(OJ_USE_SSE4_2)
229
+ const char *scanned = scan_string_SIMD(s, pi->end);
230
+ #else
231
+ const char *scanned = scan_string_noSIMD(s, pi->end);
232
+ #endif
233
+ if (scanned >= pi->end) {
200
234
  oj_set_error_at(pi,
201
235
  oj_parse_error_class,
202
236
  __FILE__,
@@ -204,7 +238,12 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
204
238
  "quoted string not terminated");
205
239
  buf_cleanup(&buf);
206
240
  return;
207
- } else if ('\\' == *s) {
241
+ }
242
+
243
+ buf_append_string(&buf, s, (size_t)(scanned - s));
244
+ s = scanned;
245
+
246
+ if ('\\' == *s) {
208
247
  s++;
209
248
  switch (*s) {
210
249
  case 'n': buf_append(&buf, '\n'); break;
@@ -273,8 +312,7 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
273
312
  buf_cleanup(&buf);
274
313
  return;
275
314
  }
276
- } else {
277
- buf_append(&buf, *s);
315
+ s++;
278
316
  }
279
317
  }
280
318
  if (0 == parent) {
@@ -327,43 +365,14 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
327
365
  buf_cleanup(&buf);
328
366
  }
329
367
 
330
- static inline void scan_string_noSIMD(ParseInfo pi) {
331
- for (; '"' != *pi->cur; pi->cur++) {
332
- if (pi->end <= pi->cur || '\0' == *pi->cur || '\\' == *pi->cur) {
333
- return;
334
- }
335
- }
336
- }
337
-
338
- #if defined(OJ_USE_SSE4_2)
339
- #include <nmmintrin.h>
340
-
341
- static inline void scan_string_SIMD(ParseInfo pi) {
342
- static const char chars[16] = "\x00\\\"";
343
- const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
344
- const char *end = (const char *)(pi->end - 16);
345
-
346
- for (; pi->cur <= end; pi->cur += 16) {
347
- const __m128i string = _mm_loadu_si128((const __m128i *)pi->cur);
348
- const int r = _mm_cmpestri(terminate, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
349
- if (r != 16) {
350
- pi->cur = (const char*)(pi->cur + r);
351
- return;
352
- }
353
- }
354
-
355
- scan_string_noSIMD(pi);
356
- }
357
- #endif
358
-
359
368
  static void read_str(ParseInfo pi) {
360
369
  const char *str = pi->cur;
361
370
  Val parent = stack_peek(&pi->stack);
362
371
 
363
372
  #if defined(OJ_USE_SSE4_2)
364
- scan_string_SIMD(pi);
373
+ pi->cur = scan_string_SIMD(pi->cur, pi->end);
365
374
  #else
366
- scan_string_noSIMD(pi);
375
+ pi->cur = scan_string_noSIMD(pi->cur, pi->end);
367
376
  #endif
368
377
  if (RB_UNLIKELY(pi->end <= pi->cur)) {
369
378
  oj_set_error_at(pi,
data/ext/oj/parser.c CHANGED
@@ -600,7 +600,7 @@ static void parse(ojParser p, const byte *json) {
600
600
  int i;
601
601
 
602
602
  p->line = 1;
603
- p->col = -1;
603
+ p->col = -1;
604
604
  #if DEBUG
605
605
  printf("*** parse - mode: %c %s\n", p->map[256], (const char *)json);
606
606
  #endif
@@ -655,7 +655,7 @@ static void parse(ojParser p, const byte *json) {
655
655
  }
656
656
  buf_append_string(&p->buf, (const char *)start, b - start);
657
657
  if ('"' == *b) {
658
- p->cur = b - json;
658
+ p->cur = b - json;
659
659
  p->funcs[p->stack[p->depth]].add_str(p);
660
660
  p->map = (0 == p->depth) ? value_map : after_map;
661
661
  break;
@@ -665,14 +665,14 @@ static void parse(ojParser p, const byte *json) {
665
665
  p->next_map = (0 == p->depth) ? value_map : after_map;
666
666
  break;
667
667
  case OPEN_OBJECT:
668
- p->cur = b - json;
668
+ p->cur = b - json;
669
669
  p->funcs[p->stack[p->depth]].open_object(p);
670
670
  p->depth++;
671
671
  p->stack[p->depth] = OBJECT_FUN;
672
672
  p->map = key1_map;
673
673
  break;
674
674
  case NUM_CLOSE_OBJECT:
675
- p->cur = b - json;
675
+ p->cur = b - json;
676
676
  calc_num(p);
677
677
  // flow through
678
678
  case CLOSE_OBJECT:
@@ -683,18 +683,18 @@ static void parse(ojParser p, const byte *json) {
683
683
  return;
684
684
  }
685
685
  p->depth--;
686
- p->cur = b - json;
686
+ p->cur = b - json;
687
687
  p->funcs[p->stack[p->depth]].close_object(p);
688
688
  break;
689
689
  case OPEN_ARRAY:
690
- p->cur = b - json;
690
+ p->cur = b - json;
691
691
  p->funcs[p->stack[p->depth]].open_array(p);
692
692
  p->depth++;
693
693
  p->stack[p->depth] = ARRAY_FUN;
694
694
  p->map = value_map;
695
695
  break;
696
696
  case NUM_CLOSE_ARRAY:
697
- p->cur = b - json;
697
+ p->cur = b - json;
698
698
  calc_num(p);
699
699
  // flow through
700
700
  case CLOSE_ARRAY:
@@ -705,11 +705,11 @@ static void parse(ojParser p, const byte *json) {
705
705
  return;
706
706
  }
707
707
  p->depth--;
708
- p->cur = b - json;
708
+ p->cur = b - json;
709
709
  p->funcs[p->stack[p->depth]].close_array(p);
710
710
  break;
711
711
  case NUM_COMMA:
712
- p->cur = b - json;
712
+ p->cur = b - json;
713
713
  calc_num(p);
714
714
  if (0 < p->depth && OBJECT_FUN == p->stack[p->depth]) {
715
715
  p->map = key_map;
@@ -872,13 +872,13 @@ static void parse(ojParser p, const byte *json) {
872
872
  p->map = big_exp_map;
873
873
  break;
874
874
  case NUM_SPC:
875
- p->cur = b - json;
876
- calc_num(p);
877
- break;
875
+ p->cur = b - json;
876
+ calc_num(p);
877
+ break;
878
878
  case NUM_NEWLINE:
879
- p->cur = b - json;
880
- calc_num(p);
881
- b++;
879
+ p->cur = b - json;
880
+ calc_num(p);
881
+ b++;
882
882
  #ifdef SPACE_JUMP
883
883
  // for (uint32_t *sj = (uint32_t*)b; 0x20202020 == *sj; sj++) { b += 4; }
884
884
  for (uint16_t *sj = (uint16_t *)b; 0x2020 == *sj; sj++) {
@@ -899,7 +899,7 @@ static void parse(ojParser p, const byte *json) {
899
899
  buf_append_string(&p->buf, (const char *)start, b - start);
900
900
  }
901
901
  if ('"' == *b) {
902
- p->cur = b - json;
902
+ p->cur = b - json;
903
903
  p->funcs[p->stack[p->depth]].add_str(p);
904
904
  p->map = p->next_map;
905
905
  break;
@@ -908,7 +908,7 @@ static void parse(ojParser p, const byte *json) {
908
908
  break;
909
909
  case STR_SLASH: p->map = esc_map; break;
910
910
  case STR_QUOTE:
911
- p->cur = b - json;
911
+ p->cur = b - json;
912
912
  p->funcs[p->stack[p->depth]].add_str(p);
913
913
  p->map = p->next_map;
914
914
  break;
@@ -986,7 +986,7 @@ static void parse(ojParser p, const byte *json) {
986
986
  case VAL_NULL:
987
987
  if ('u' == b[1] && 'l' == b[2] && 'l' == b[3]) {
988
988
  b += 3;
989
- p->cur = b - json;
989
+ p->cur = b - json;
990
990
  p->funcs[p->stack[p->depth]].add_null(p);
991
991
  p->map = (0 == p->depth) ? value_map : after_map;
992
992
  break;
@@ -1012,7 +1012,7 @@ static void parse(ojParser p, const byte *json) {
1012
1012
  case VAL_TRUE:
1013
1013
  if ('r' == b[1] && 'u' == b[2] && 'e' == b[3]) {
1014
1014
  b += 3;
1015
- p->cur = b - json;
1015
+ p->cur = b - json;
1016
1016
  p->funcs[p->stack[p->depth]].add_true(p);
1017
1017
  p->map = (0 == p->depth) ? value_map : after_map;
1018
1018
  break;
@@ -1038,7 +1038,7 @@ static void parse(ojParser p, const byte *json) {
1038
1038
  case VAL_FALSE:
1039
1039
  if ('a' == b[1] && 'l' == b[2] && 's' == b[3] && 'e' == b[4]) {
1040
1040
  b += 4;
1041
- p->cur = b - json;
1041
+ p->cur = b - json;
1042
1042
  p->funcs[p->stack[p->depth]].add_false(p);
1043
1043
  p->map = (0 == p->depth) ? value_map : after_map;
1044
1044
  break;
@@ -1072,7 +1072,7 @@ static void parse(ojParser p, const byte *json) {
1072
1072
  parse_error(p, "expected null");
1073
1073
  return;
1074
1074
  }
1075
- p->cur = b - json;
1075
+ p->cur = b - json;
1076
1076
  p->funcs[p->stack[p->depth]].add_null(p);
1077
1077
  p->map = (0 == p->depth) ? value_map : after_map;
1078
1078
  }
@@ -1084,7 +1084,7 @@ static void parse(ojParser p, const byte *json) {
1084
1084
  parse_error(p, "expected false");
1085
1085
  return;
1086
1086
  }
1087
- p->cur = b - json;
1087
+ p->cur = b - json;
1088
1088
  p->funcs[p->stack[p->depth]].add_false(p);
1089
1089
  p->map = (0 == p->depth) ? value_map : after_map;
1090
1090
  }
@@ -1096,7 +1096,7 @@ static void parse(ojParser p, const byte *json) {
1096
1096
  parse_error(p, "expected true");
1097
1097
  return;
1098
1098
  }
1099
- p->cur = b - json;
1099
+ p->cur = b - json;
1100
1100
  p->funcs[p->stack[p->depth]].add_true(p);
1101
1101
  p->map = (0 == p->depth) ? value_map : after_map;
1102
1102
  }
@@ -1114,6 +1114,9 @@ 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
+ }
1117
1120
  if (0 == p->depth) {
1118
1121
  switch (p->map[256]) {
1119
1122
  case '0':
@@ -1125,9 +1128,9 @@ static void parse(ojParser p, const byte *json) {
1125
1128
  case 'g':
1126
1129
  case 'B':
1127
1130
  case 'Y':
1128
- p->cur = b - json;
1129
- calc_num(p);
1130
- break;
1131
+ p->cur = b - json;
1132
+ calc_num(p);
1133
+ break;
1131
1134
  }
1132
1135
  }
1133
1136
  return;
data/ext/oj/validate.c CHANGED
@@ -2,50 +2,45 @@
2
2
 
3
3
  #include "parser.h"
4
4
 
5
- static void
6
- noop(ojParser p) {
5
+ static void noop(ojParser p) {
7
6
  }
8
7
 
9
- static VALUE
10
- option(ojParser p, const char *key, VALUE value) {
8
+ static VALUE option(ojParser p, const char *key, VALUE value) {
11
9
  rb_raise(rb_eArgError, "%s is not an option for the validate delegate", key);
12
10
  return Qnil;
13
11
  }
14
12
 
15
- static VALUE
16
- result(ojParser p) {
13
+ static VALUE result(ojParser p) {
17
14
  return Qnil;
18
15
  }
19
16
 
20
- static void
21
- dfree(ojParser p) {
17
+ static void dfree(ojParser p) {
22
18
  }
23
19
 
24
- static void
25
- mark(ojParser p) {
20
+ static void mark(ojParser p) {
26
21
  }
27
22
 
28
23
  void oj_set_parser_validator(ojParser p) {
29
- p->ctx = NULL;
30
- Funcs end = p->funcs + 3;
24
+ Funcs end = p->funcs + 3;
31
25
  Funcs f;
26
+ p->ctx = NULL;
32
27
 
33
28
  for (f = p->funcs; f < end; f++) {
34
- f->add_null = noop;
35
- f->add_true = noop;
36
- f->add_false = noop;
37
- f->add_int = noop;
38
- f->add_float = noop;
39
- f->add_big = noop;
40
- f->add_str = noop;
41
- f->open_array = noop;
42
- f->close_array = noop;
43
- f->open_object = noop;
44
- f->close_object = noop;
29
+ f->add_null = noop;
30
+ f->add_true = noop;
31
+ f->add_false = noop;
32
+ f->add_int = noop;
33
+ f->add_float = noop;
34
+ f->add_big = noop;
35
+ f->add_str = noop;
36
+ f->open_array = noop;
37
+ f->close_array = noop;
38
+ f->open_object = noop;
39
+ f->close_object = noop;
45
40
  }
46
41
  p->option = option;
47
42
  p->result = result;
48
- p->free = dfree;
49
- p->mark = mark;
50
- p->start = noop;
43
+ p->free = dfree;
44
+ p->mark = mark;
45
+ p->start = noop;
51
46
  }
data/lib/oj/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '3.13.16'
4
+ VERSION = '3.13.17'
5
5
  end
data/test/bar.rb CHANGED
@@ -6,11 +6,6 @@ $: << File.join(File.dirname(__FILE__), "../ext")
6
6
 
7
7
  require 'oj'
8
8
 
9
- puts Oj.dump({
10
- "float_test" => 0.25,
11
- "nan_test" => Float::NAN,
12
- "inf_test" => Float::INFINITY,
13
- "minus_inf_test" => -Float::INFINITY,
14
- "min_test" => Float::MIN,
15
- "max_test" => Float::MAX,
16
- }, mode: :object) # => {"float_test":0.25,"nan_test":3.3e14159265358979323846
9
+ p = Oj::Parser.validate
10
+ # p = Oj::Parser.new(:debug)
11
+ p.parse(%|{|)
data/test/test_compat.rb CHANGED
@@ -504,6 +504,15 @@ class CompatJuice < Minitest::Test
504
504
  assert_equal("ぴーたー", Oj.load(json)['a'])
505
505
  end
506
506
 
507
+ def test_parse_large_escaped_string
508
+ invalid_json = %|{"a":\"aaaa\\nbbbb\\rcccc\\tddd\\feee\\bf\/\\\\\\u3074\\u30fc\\u305f\\u30fc }|
509
+ error = assert_raises() { Oj.load(invalid_json) }
510
+ assert(error.message.include?('quoted string not terminated'))
511
+
512
+ json = "\"aaaa\\nbbbb\\rcccc\\tddd\\feee\\bf\/\\\\\\u3074\\u30fc\\u305f\\u30fc \""
513
+ assert_equal("aaaa\nbbbb\rcccc\tddd\feee\bf/\\ぴーたー ", Oj.load(json))
514
+ end
515
+
507
516
  def dump_and_load(obj, trace=false)
508
517
  json = Oj.dump(obj)
509
518
  puts json if trace
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.13.16
4
+ version: 3.13.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-06 00:00:00.000000000 Z
11
+ date: 2022-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler