summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--internal.h1
-rw-r--r--re.c77
-rw-r--r--string.c19
-rw-r--r--test/ruby/test_string.rb5
5 files changed, 98 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 76afc07d1b..0866aaffcb 100644
--- a/NEWS
+++ b/NEWS
@@ -108,6 +108,7 @@ with all sufficient information, see the ChangeLog file or Redmine
* String#delete_suffix! is added to remove suffix destructively [Feature #13665]
* String#each_grapheme_cluster and String#grapheme_clusters is added to
enumerate grapheme clusters [Feature #13780]
+ * String#start_with? supports regexp [Feature #13712]
* Regexp/String: Updated Unicode version from 9.0.0 to 10.0.0 [Feature #13685]
diff --git a/internal.h b/internal.h
index 0b2e6366ee..4ac024d9af 100644
--- a/internal.h
+++ b/internal.h
@@ -1570,6 +1570,7 @@ VALUE rb_reg_compile(VALUE str, int options, const char *sourcefile, int sourcel
VALUE rb_reg_check_preprocess(VALUE);
long rb_reg_search0(VALUE, VALUE, long, int, int);
VALUE rb_reg_match_p(VALUE re, VALUE str, long pos);
+bool rb_reg_start_with_p(VALUE re, VALUE str);
void rb_backref_set_string(VALUE string, long pos, long len);
int rb_match_count(VALUE match);
int rb_match_nth_defined(int nth, VALUE match);
diff --git a/re.c b/re.c
index c1c29aea37..a78672b6bb 100644
--- a/re.c
+++ b/re.c
@@ -1580,6 +1580,83 @@ rb_reg_search(VALUE re, VALUE str, long pos, int reverse)
return rb_reg_search0(re, str, pos, reverse, 1);
}
+bool
+rb_reg_start_with_p(VALUE re, VALUE str)
+{
+ long result;
+ VALUE match;
+ struct re_registers regi, *regs = &regi;
+ regex_t *reg;
+ int tmpreg;
+ onig_errmsg_buffer err = "";
+
+ reg = rb_reg_prepare_re0(re, str, err);
+ tmpreg = reg != RREGEXP_PTR(re);
+ if (!tmpreg) RREGEXP(re)->usecnt++;
+
+ match = rb_backref_get();
+ if (!NIL_P(match)) {
+ if (FL_TEST(match, MATCH_BUSY)) {
+ match = Qnil;
+ }
+ else {
+ regs = RMATCH_REGS(match);
+ }
+ }
+ if (NIL_P(match)) {
+ MEMZERO(regs, struct re_registers, 1);
+ }
+ result = onig_match(reg,
+ (UChar*)(RSTRING_PTR(str)),
+ ((UChar*)(RSTRING_PTR(str)) + RSTRING_LEN(str)),
+ (UChar*)(RSTRING_PTR(str)),
+ regs, ONIG_OPTION_NONE);
+ if (!tmpreg) RREGEXP(re)->usecnt--;
+ if (tmpreg) {
+ if (RREGEXP(re)->usecnt) {
+ onig_free(reg);
+ }
+ else {
+ onig_free(RREGEXP_PTR(re));
+ RREGEXP_PTR(re) = reg;
+ }
+ }
+ if (result < 0) {
+ if (regs == &regi)
+ onig_region_free(regs, 0);
+ if (result == ONIG_MISMATCH) {
+ rb_backref_set(Qnil);
+ return false;
+ }
+ else {
+ onig_error_code_to_str((UChar*)err, (int)result);
+ rb_reg_raise(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), err, re);
+ }
+ }
+
+ if (NIL_P(match)) {
+ int err;
+ match = match_alloc(rb_cMatch);
+ err = rb_reg_region_copy(RMATCH_REGS(match), regs);
+ onig_region_free(regs, 0);
+ if (err) rb_memerror();
+ }
+ else {
+ FL_UNSET(match, FL_TAINT);
+ }
+
+ RMATCH(match)->str = rb_str_new4(str);
+ OBJ_INFECT(match, str);
+
+ RMATCH(match)->regexp = re;
+ RMATCH(match)->rmatch->char_offset_updated = 0;
+ rb_backref_set(match);
+
+ OBJ_INFECT(match, re);
+
+ return true;
+}
+
VALUE
rb_reg_nth_defined(int nth, VALUE match)
{
diff --git a/string.c b/string.c
index 27d40fbe26..bea38df4bc 100644
--- a/string.c
+++ b/string.c
@@ -9219,11 +9219,20 @@ rb_str_start_with(int argc, VALUE *argv, VALUE str)
for (i=0; i<argc; i++) {
VALUE tmp = argv[i];
- 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)
- return Qtrue;
+ switch (TYPE(tmp)) {
+ case T_REGEXP:
+ {
+ bool r = rb_reg_start_with_p(tmp, str);
+ if (r) return Qtrue;
+ }
+ break;
+ default:
+ 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)
+ return Qtrue;
+ }
}
return Qfalse;
}
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 75da42eeef..b6c6214364 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -1723,6 +1723,11 @@ CODE
bug5536 = '[ruby-core:40623]'
assert_raise(TypeError, bug5536) {S("str").start_with? :not_convertible_to_string}
+
+ assert_equal(true, "hello".start_with?(/hel/))
+ assert_equal("hel", $&)
+ assert_equal(false, "hello".start_with?(/el/))
+ assert_nil($&)
end
def test_strip