mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Improve performance of String#capitalize with a single-byte ASCII fast path
#upcase/#downcase already case-map ASCII strings with a plain byte loop, but #capitalize/#capitalize! always went through full Unicode case-mapping, making them ~10x slower on the same input. Add capitalize_single (mirroring upcase_single/downcase_single), taken whenever case_option_single_p() holds: it titlecases the first byte and downcases the rest, touching ASCII letters only.
This commit is contained in:
1 parent
1165d7c6ba
commit
217dd3d1fb
2 files changed
+41
-2
Notes:
git
2026-06-20 07:15:12 +00:00
Merged: https://github.com/ruby/ruby/pull/17403 Merged-By: nobu <nobu@ruby-lang.org>
No files matched your search
@@ -8,3 +8,6 @@ benchmark:
|
||||
capitalize-10: str10.capitalize
|
||||
capitalize-100: str100.capitalize
|
||||
capitalize-1000: str1000.capitalize
|
||||
capitalize!-1: str1.dup.capitalize!
|
||||
capitalize!-100: str100.dup.capitalize!
|
||||
capitalize!-1000: str1000.dup.capitalize!
|
||||
@@ -8158,6 +8158,33 @@ rb_str_downcase(int argc, VALUE *argv, VALUE str)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
capitalize_single(VALUE str)
|
||||
{
|
||||
char *s = RSTRING_PTR(str), *send = RSTRING_END(str);
|
||||
bool modified = false;
|
||||
|
||||
if (s < send) {
|
||||
unsigned int c = (unsigned char)*s;
|
||||
|
||||
if ('a' <= c && c <= 'z') {
|
||||
*s = 'A' + (c - 'a');
|
||||
modified = true;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
while (s < send) {
|
||||
unsigned int c = (unsigned char)*s;
|
||||
|
||||
if ('A' <= c && c <= 'Z') {
|
||||
*s = 'a' + (c - 'A');
|
||||
modified = true;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
@@ -8181,7 +8208,11 @@ rb_str_capitalize_bang(int argc, VALUE *argv, VALUE str)
|
||||
str_modify_keep_cr(str);
|
||||
enc = str_true_enc(str);
|
||||
if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return Qnil;
|
||||
if (flags&ONIGENC_CASE_ASCII_ONLY)
|
||||
if (case_option_single_p(flags, enc, str)) {
|
||||
if (capitalize_single(str))
|
||||
flags |= ONIGENC_CASE_MODIFIED;
|
||||
}
|
||||
else if (flags&ONIGENC_CASE_ASCII_ONLY)
|
||||
rb_str_ascii_casemap(str, str, &flags, enc);
|
||||
else
|
||||
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
||||
@@ -8209,7 +8240,12 @@ rb_str_capitalize(int argc, VALUE *argv, VALUE str)
|
||||
flags = check_case_options(argc, argv, flags);
|
||||
enc = str_true_enc(str);
|
||||
if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return str;
|
||||
if (flags&ONIGENC_CASE_ASCII_ONLY) {
|
||||
if (case_option_single_p(flags, enc, str)) {
|
||||
ret = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
|
||||
str_enc_copy_direct(ret, str);
|
||||
capitalize_single(ret);
|
||||
}
|
||||
else if (flags&ONIGENC_CASE_ASCII_ONLY) {
|
||||
ret = rb_str_new(0, RSTRING_LEN(str));
|
||||
rb_str_ascii_casemap(str, ret, &flags, enc);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user