diff --git a/benchmark/string_capitalize.yml b/benchmark/string_capitalize.yml index 7d23fd3d35e..585171064ce 100644 --- a/benchmark/string_capitalize.yml +++ b/benchmark/string_capitalize.yml @@ -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! diff --git a/string.c b/string.c index 408f899edba..bd729401597 100644 --- a/string.c +++ b/string.c @@ -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); }