summaryrefslogtreecommitdiff
diff options
-rw-r--r--spec/ruby/core/string/start_with_spec.rb10
-rw-r--r--spec/ruby/shared/string/start_with.rb6
-rw-r--r--string.c56
-rw-r--r--test/ruby/test_string.rb6
4 files changed, 59 insertions, 19 deletions
diff --git a/spec/ruby/core/string/start_with_spec.rb b/spec/ruby/core/string/start_with_spec.rb
index 3833289f96..81eed47f96 100644
--- a/spec/ruby/core/string/start_with_spec.rb
+++ b/spec/ruby/core/string/start_with_spec.rb
@@ -7,12 +7,14 @@ describe "String#start_with?" do
it_behaves_like :start_with, :to_s
# Here and not in the shared examples because this is invalid as a Symbol
- it "does not check that we are not starting to match at the head of a character" do
+ it "matches part of a character with the same part" do
"\xA9".should.start_with?("\xA9") # A9 is not a character head for UTF-8
end
- it "does not check we are matching only part of a character" do
- "\xe3\x81\x82".size.should == 1
- "\xe3\x81\x82".should.start_with?("\xe3")
+ ruby_bug "#19784", ""..."3.3" do
+ it "checks we are matching only part of a character" do
+ "\xe3\x81\x82".size.should == 1
+ "\xe3\x81\x82".should_not.start_with?("\xe3")
+ end
end
end
diff --git a/spec/ruby/shared/string/start_with.rb b/spec/ruby/shared/string/start_with.rb
index 6932a017b6..91fc50c4cd 100644
--- a/spec/ruby/shared/string/start_with.rb
+++ b/spec/ruby/shared/string/start_with.rb
@@ -70,7 +70,9 @@ describe :start_with, shared: true do
$1.should be_nil
end
- it "does not check that we are not matching part of a character" do
- "\xC3\xA9".send(@method).should.start_with?("\xC3")
+ ruby_bug "#19784", ""..."3.3" do
+ it "checks that we are not matching part of a character" do
+ "\xC3\xA9".send(@method).should_not.start_with?("\xC3")
+ end
end
end
diff --git a/string.c b/string.c
index b65868a226..5af5fc4a40 100644
--- a/string.c
+++ b/string.c
@@ -10461,10 +10461,20 @@ rb_str_start_with(int argc, VALUE *argv, VALUE str)
return Qtrue;
}
else {
+ const char *p, *s, *e;
+ long slen, tlen;
+ rb_encoding *enc;
+
StringValue(tmp);
- rb_enc_check(str, tmp);
- if (RSTRING_LEN(str) < RSTRING_LEN(tmp)) continue;
- if (memcmp(RSTRING_PTR(str), RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
+ enc = rb_enc_check(str, tmp);
+ if ((tlen = RSTRING_LEN(tmp)) == 0) return Qtrue;
+ if ((slen = RSTRING_LEN(str)) < tlen) continue;
+ p = RSTRING_PTR(str);
+ e = p + slen;
+ s = p + tlen;
+ if (!at_char_boundary(p, s, e, enc))
+ continue;
+ if (memcmp(p, RSTRING_PTR(tmp), tlen) == 0)
return Qtrue;
}
}
@@ -10483,12 +10493,13 @@ static VALUE
rb_str_end_with(int argc, VALUE *argv, VALUE str)
{
int i;
- char *p, *s, *e;
- rb_encoding *enc;
for (i=0; i<argc; i++) {
VALUE tmp = argv[i];
+ const char *p, *s, *e;
long slen, tlen;
+ rb_encoding *enc;
+
StringValue(tmp);
enc = rb_enc_check(str, tmp);
if ((tlen = RSTRING_LEN(tmp)) == 0) return Qtrue;
@@ -10498,7 +10509,7 @@ rb_str_end_with(int argc, VALUE *argv, VALUE str)
s = e - tlen;
if (!at_char_boundary(p, s, e, enc))
continue;
- if (memcmp(s, RSTRING_PTR(tmp), RSTRING_LEN(tmp)) == 0)
+ if (memcmp(s, RSTRING_PTR(tmp), tlen) == 0)
return Qtrue;
}
return Qfalse;
@@ -10516,12 +10527,17 @@ rb_str_end_with(int argc, VALUE *argv, VALUE str)
static long
deleted_prefix_length(VALUE str, VALUE prefix)
{
- char *strptr, *prefixptr;
+ const char *strptr, *prefixptr;
long olen, prefixlen;
+ rb_encoding *enc = rb_enc_get(str);
StringValue(prefix);
- if (is_broken_string(prefix)) return 0;
- rb_enc_check(str, prefix);
+
+ if (!is_broken_string(prefix) ||
+ !rb_enc_asciicompat(enc) ||
+ !rb_enc_asciicompat(rb_enc_get(prefix))) {
+ enc = rb_enc_check(str, prefix);
+ }
/* return 0 if not start with prefix */
prefixlen = RSTRING_LEN(prefix);
@@ -10531,6 +10547,19 @@ deleted_prefix_length(VALUE str, VALUE prefix)
strptr = RSTRING_PTR(str);
prefixptr = RSTRING_PTR(prefix);
if (memcmp(strptr, prefixptr, prefixlen) != 0) return 0;
+ if (is_broken_string(prefix)) {
+ if (!is_broken_string(str)) {
+ /* prefix in a valid string cannot be broken */
+ return 0;
+ }
+ const char *strend = strptr + olen;
+ const char *after_prefix = strptr + prefixlen;
+ if (!at_char_boundary(strptr, after_prefix, strend, enc)) {
+ /* prefix does not end at char-boundary */
+ return 0;
+ }
+ }
+ /* prefix part in `str` also should be valid. */
return prefixlen;
}
@@ -10587,7 +10616,7 @@ rb_str_delete_prefix(VALUE str, VALUE prefix)
static long
deleted_suffix_length(VALUE str, VALUE suffix)
{
- char *strptr, *suffixptr, *s;
+ const char *strptr, *suffixptr;
long olen, suffixlen;
rb_encoding *enc;
@@ -10602,9 +10631,10 @@ deleted_suffix_length(VALUE str, VALUE suffix)
if (olen < suffixlen) return 0;
strptr = RSTRING_PTR(str);
suffixptr = RSTRING_PTR(suffix);
- s = strptr + olen - suffixlen;
- if (memcmp(s, suffixptr, suffixlen) != 0) return 0;
- if (!at_char_boundary(strptr, s, strptr + olen, enc)) return 0;
+ const char *strend = strptr + olen;
+ const char *before_suffix = strend - suffixlen;
+ if (memcmp(before_suffix, suffixptr, suffixlen) != 0) return 0;
+ if (!at_char_boundary(strptr, before_suffix, strend, enc)) return 0;
return suffixlen;
}
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index e69c66be40..1d8902baf1 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -1938,6 +1938,8 @@ CODE
assert_send([S("hello"), :start_with?, S("hel")])
assert_not_send([S("hello"), :start_with?, S("el")])
assert_send([S("hello"), :start_with?, S("el"), S("he")])
+ assert_send([S("\xFF\xFE"), :start_with?, S("\xFF")])
+ assert_not_send([S("\u{c4}"), :start_with?, S("\xC3")])
bug5536 = '[ruby-core:40623]'
assert_raise(TypeError, bug5536) {S("str").start_with? :not_convertible_to_string}
@@ -2930,6 +2932,7 @@ CODE
assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s.delete_prefix("\x95"))
assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+ assert_equal("\xFE", S("\xFF\xFE").delete_prefix("\xFF"))
end
def test_delete_prefix_clear_coderange
@@ -2978,6 +2981,9 @@ CODE
assert_equal(nil, s.delete_prefix!("\xe3"))
assert_equal("\xe3\x81\x82", s)
+ s = S("\xFF\xFE")
+ assert_equal("\xFE", s.delete_prefix!("\xFF"))
+ assert_equal("\xFE", s)
end
def test_delete_prefix_bang_clear_coderange