oj 3.13.16 → 3.13.17

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: 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