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:
Sampo Kuokkanen authored and GitHub committed 2026-06-20 16:14:45 +09:00
1 parent 1165d7c6ba
commit 217dd3d1fb
2 files changed
+41 -2
Notes: git 2026-06-20 07:15:12 +00:00

No files matched your search

+3
View File
@@ -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!
+38 -2
View File
@@ -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);
}