String#sub and siblings should not use regex when String pattern is passed

Currently String#sub, #sub!, #gsub, and#gsub!all accept a String pattern, but immediately create a Regexp from it, and use the regex engine to search for the pattern. This is not performant. For example,"123:456".gsub(":", "_")` creates the following objects, most of which are immediately up for GC:

dup of the original String

result String

2x ":"<US-ASCII>

2x ":"<ASCII-8BIT>

Regexp from pattern: /:/

#<MatchData ":">

#<MatchData nil>

I have a solution which is not too complicated, at https://github.com/ruby/ruby/pull/579 and attached. Calls to rb_reg_search() are replaced with calls to a new function, rb_pat_search(), which conditionally calls rb_reg_search() or rb_str_index(), depending on whether the pattern is a String. Calculating the substring that needs to be replaced is also different when the pattern is a String.

There is only one incompatibility that I am aware of: $& will not be set after using a sub method with a String pattern. (Subgroups ($1, ...) will not be available either, but weren't before, since String patterns are escaped before being used.)

In the future, only 3 more methods use the function, get_pat(), that creates a Regexp from the String pattern: #split, #scan, and #match. I think this fix could be applied to these as well.

I think the speedup in this patch comes almost entirely from skipping the regex engine, not from the GC savings.

Preserving $& (and $~ and friends) while still not firing up the regex engine might be possible (constructing the basic backref, with no subgroups, by hand), but very very ugly code (an RMatch has an RRegexp and an rmatch which has a re_registers, etc). This might only a ~20 line function, but feels so dirty...

I think an improvement (or replacement) to reg_cache would also be welcome.

Good point, Benoit! This is actually why I started looking into #gsub in the first place. I benchmarked ActiveSupport::Inflector [1], which does operations like gsub!('/', '::') and gsub('::', '/'). Here are the benchmarks, before and after Nobu's patch, based on my patch: