diff --git a/ext/jruby/org/jruby/ext/strscan/RubyStringScanner.java b/ext/jruby/org/jruby/ext/strscan/RubyStringScanner.java index d8e3cb0f0a..7ca0a6af2b 100644 --- a/ext/jruby/org/jruby/ext/strscan/RubyStringScanner.java +++ b/ext/jruby/org/jruby/ext/strscan/RubyStringScanner.java @@ -688,7 +688,15 @@ public IRubyObject matched(ThreadContext context) { public IRubyObject matched_size(ThreadContext context) { check(context); if (!isMatched()) return context.nil; - return RubyFixnum.newFixnum(context.runtime, regs.getEnd(0) - regs.getBeg(0)); + + int size = str.size(); + int beg = adjustRegisterPosition(regs.getBeg(0)); + int end = adjustRegisterPosition(regs.getEnd(0)); + + if (beg > size) return context.nil; + if (end > size) end = size; + + return RubyFixnum.newFixnum(context.runtime, end - beg); } @JRubyMethod(name = "[]") diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c index e879a7f7fa..d46e15f7dc 100644 --- a/ext/strscan/strscan.c +++ b/ext/strscan/strscan.c @@ -1673,10 +1673,14 @@ static VALUE strscan_matched_size(VALUE self) { struct strscanner *p; + long beg, end; GET_SCANNER(self, p); if (! MATCHED_P(p)) return Qnil; - return LONG2NUM(p->regs.end[0] - p->regs.beg[0]); + beg = adjust_register_position(p, p->regs.beg[0]); + if (beg > S_LEN(p)) return Qnil; + end = minl(adjust_register_position(p, p->regs.end[0]), S_LEN(p)); + return LONG2NUM(end - beg); } static int diff --git a/lib/strscan/truffleruby.rb b/lib/strscan/truffleruby.rb index 691a951b19..55c1759356 100644 --- a/lib/strscan/truffleruby.rb +++ b/lib/strscan/truffleruby.rb @@ -115,7 +115,15 @@ def rest? def matched? = !Primitive.nil?(@last_match) - def matched = @last_match&.to_s + def matched + return unless @last_match + + beg = Primitive.match_data_byte_begin(@last_match, 0) + return if beg > @string.bytesize + + fin = [Primitive.match_data_byte_end(@last_match, 0), @string.bytesize].min + @string.byteslice(beg, fin - beg) + end def [](group) raise TypeError, 'no implicit conversion of Range into Integer' if Primitive.is_a?(group, Range) @@ -151,7 +159,9 @@ def named_captures = @last_match&.named_captures || {} def matched_size if @last_match - Primitive.match_data_byte_end(@last_match, 0) - Primitive.match_data_byte_begin(@last_match, 0) + beg = Primitive.match_data_byte_begin(@last_match, 0) + return if beg > @string.bytesize + [Primitive.match_data_byte_end(@last_match, 0), @string.bytesize].min - beg end end diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb index 79784b59f5..8bf24c7301 100644 --- a/test/strscan/test_stringscanner.rb +++ b/test/strscan/test_stringscanner.rb @@ -738,6 +738,27 @@ def test_matched_size assert_nil(s.matched_size) end + def test_matched_size_when_shrunk + # matched_size must agree with matched, which extract_range clamps to the + # current length of the stored string. + s = create_string_scanner(+"before 29 after") + s.skip_until(" ") + assert_equal("29", s.scan(/\d+/)) + assert_equal(2, s.matched_size) + + s.string.replace("before 2") + assert_equal("2", s.matched) + assert_equal(1, s.matched_size) + + s.string.replace("before ") + assert_equal("", s.matched) + assert_equal(0, s.matched_size) + + s.string.replace("before") + assert_nil(s.matched) + assert_nil(s.matched_size) + end + def test_empty_encoding_utf8 ss = create_string_scanner('') assert_equal(Encoding::UTF_8, ss.rest.encoding)