mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Improve performance of case-conversion methods
This commit is contained in:
parent
1f349ea297
commit
77440e949b
5 changed files with 200 additions and 57 deletions
10
benchmark/string_capitalize.yml
Normal file
10
benchmark/string_capitalize.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
prelude: |
|
||||||
|
str1 = [*"a".."m",*"N".."Z",*"0".."9"].join("")
|
||||||
|
str10 = str1 * 10
|
||||||
|
str100 = str10 * 10
|
||||||
|
str1000 = str100 * 10
|
||||||
|
benchmark:
|
||||||
|
capitalize-1: str1.capitalize
|
||||||
|
capitalize-10: str10.capitalize
|
||||||
|
capitalize-100: str100.capitalize
|
||||||
|
capitalize-1000: str1000.capitalize
|
10
benchmark/string_downcase.yml
Normal file
10
benchmark/string_downcase.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
prelude: |
|
||||||
|
str1 = [*"A".."Z",*"0".."9"].join("")
|
||||||
|
str10 = str1 * 10
|
||||||
|
str100 = str10 * 10
|
||||||
|
str1000 = str100 * 10
|
||||||
|
benchmark:
|
||||||
|
downcase-1: str1.upcase
|
||||||
|
downcase-10: str10.upcase
|
||||||
|
downcase-100: str100.upcase
|
||||||
|
downcase-1000: str1000.upcase
|
10
benchmark/string_swapcase.yml
Normal file
10
benchmark/string_swapcase.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
prelude: |
|
||||||
|
str1 = [*"A".."M",*"n".."z",*"0".."9"].join("")
|
||||||
|
str10 = str1 * 10
|
||||||
|
str100 = str10 * 10
|
||||||
|
str1000 = str100 * 10
|
||||||
|
benchmark:
|
||||||
|
swapcase-1: str1.swapcase
|
||||||
|
swapcase-10: str10.swapcase
|
||||||
|
swapcase-100: str100.swapcase
|
||||||
|
swapcase-1000: str1000.swapcase
|
10
benchmark/string_upcase.yml
Normal file
10
benchmark/string_upcase.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
prelude: |
|
||||||
|
str1 = [*"a".."z",*"0".."9"].join("")
|
||||||
|
str10 = str1 * 10
|
||||||
|
str100 = str10 * 10
|
||||||
|
str1000 = str100 * 10
|
||||||
|
benchmark:
|
||||||
|
upcase-1: str1.upcase
|
||||||
|
upcase-10: str10.upcase
|
||||||
|
upcase-100: str100.upcase
|
||||||
|
upcase-1000: str1000.upcase
|
217
string.c
217
string.c
|
@ -6408,6 +6408,14 @@ rb_str_check_dummy_enc(rb_encoding *enc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static rb_encoding *
|
||||||
|
str_true_enc(VALUE str)
|
||||||
|
{
|
||||||
|
rb_encoding *enc = STR_ENC_GET(str);
|
||||||
|
rb_str_check_dummy_enc(enc);
|
||||||
|
return enc;
|
||||||
|
}
|
||||||
|
|
||||||
static OnigCaseFoldType
|
static OnigCaseFoldType
|
||||||
check_case_options(int argc, VALUE *argv, OnigCaseFoldType flags)
|
check_case_options(int argc, VALUE *argv, OnigCaseFoldType flags)
|
||||||
{
|
{
|
||||||
|
@ -6448,6 +6456,14 @@ check_case_options(int argc, VALUE *argv, OnigCaseFoldType flags)
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
case_option_single_p(OnigCaseFoldType flags, rb_encoding *enc, VALUE str)
|
||||||
|
{
|
||||||
|
if ((flags & ONIGENC_CASE_ASCII_ONLY) && (rb_enc_mbmaxlen(enc) == 1))
|
||||||
|
return true;
|
||||||
|
return !(flags & ONIGENC_CASE_FOLD_TURKISH_AZERI) && ENC_CODERANGE(str) == ENC_CODERANGE_7BIT;
|
||||||
|
}
|
||||||
|
|
||||||
/* 16 should be long enough to absorb any kind of single character length increase */
|
/* 16 should be long enough to absorb any kind of single character length increase */
|
||||||
#define CASE_MAPPING_ADDITIONAL_LENGTH 20
|
#define CASE_MAPPING_ADDITIONAL_LENGTH 20
|
||||||
#ifndef CASEMAP_DEBUG
|
#ifndef CASEMAP_DEBUG
|
||||||
|
@ -6484,7 +6500,7 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
|
||||||
{
|
{
|
||||||
VALUE target;
|
VALUE target;
|
||||||
|
|
||||||
OnigUChar *source_current, *source_end;
|
const OnigUChar *source_current, *source_end;
|
||||||
int target_length = 0;
|
int target_length = 0;
|
||||||
VALUE buffer_anchor;
|
VALUE buffer_anchor;
|
||||||
mapping_buffer *current_buffer = 0;
|
mapping_buffer *current_buffer = 0;
|
||||||
|
@ -6554,21 +6570,30 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static VALUE
|
||||||
rb_str_ascii_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
|
rb_str_ascii_casemap(VALUE source, VALUE target, OnigCaseFoldType *flags, rb_encoding *enc)
|
||||||
{
|
{
|
||||||
OnigUChar *source_current, *source_end;
|
const OnigUChar *source_current, *source_end;
|
||||||
|
OnigUChar *target_current, *target_end;
|
||||||
long old_length = RSTRING_LEN(source);
|
long old_length = RSTRING_LEN(source);
|
||||||
int length_or_invalid;
|
int length_or_invalid;
|
||||||
|
|
||||||
if (old_length == 0) return;
|
if (old_length == 0) return Qnil;
|
||||||
|
|
||||||
source_current = (OnigUChar*)RSTRING_PTR(source);
|
source_current = (OnigUChar*)RSTRING_PTR(source);
|
||||||
source_end = (OnigUChar*)RSTRING_END(source);
|
source_end = (OnigUChar*)RSTRING_END(source);
|
||||||
|
if (source == target) {
|
||||||
|
target_current = (OnigUChar*)source_current;
|
||||||
|
target_end = (OnigUChar*)source_end;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target_current = (OnigUChar*)RSTRING_PTR(target);
|
||||||
|
target_end = (OnigUChar*)RSTRING_END(target);
|
||||||
|
}
|
||||||
|
|
||||||
length_or_invalid = onigenc_ascii_only_case_map(flags,
|
length_or_invalid = onigenc_ascii_only_case_map(flags,
|
||||||
(const OnigUChar**)&source_current, source_end,
|
&source_current, source_end,
|
||||||
source_current, source_end, enc);
|
target_current, target_end, enc);
|
||||||
if (length_or_invalid < 0)
|
if (length_or_invalid < 0)
|
||||||
rb_raise(rb_eArgError, "input string invalid");
|
rb_raise(rb_eArgError, "input string invalid");
|
||||||
if (CASEMAP_DEBUG && length_or_invalid != old_length) {
|
if (CASEMAP_DEBUG && length_or_invalid != old_length) {
|
||||||
|
@ -6577,6 +6602,29 @@ rb_str_ascii_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc)
|
||||||
rb_raise(rb_eArgError, "internal problem with rb_str_ascii_casemap"
|
rb_raise(rb_eArgError, "internal problem with rb_str_ascii_casemap"
|
||||||
"; old_length=%ld, new_length=%d\n", old_length, length_or_invalid);
|
"; old_length=%ld, new_length=%d\n", old_length, length_or_invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBJ_INFECT_RAW(target, source);
|
||||||
|
str_enc_copy(target, source);
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
upcase_single(VALUE str)
|
||||||
|
{
|
||||||
|
char *s = RSTRING_PTR(str), *send = RSTRING_END(str);
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
|
while (s < send) {
|
||||||
|
unsigned int c = *(unsigned char*)s;
|
||||||
|
|
||||||
|
if (rb_enc_isascii(c, enc) && 'a' <= c && c <= 'z') {
|
||||||
|
*s = 'A' + (c - 'a');
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -6598,24 +6646,13 @@ rb_str_upcase_bang(int argc, VALUE *argv, VALUE str)
|
||||||
|
|
||||||
flags = check_case_options(argc, argv, flags);
|
flags = check_case_options(argc, argv, flags);
|
||||||
str_modify_keep_cr(str);
|
str_modify_keep_cr(str);
|
||||||
enc = STR_ENC_GET(str);
|
enc = str_true_enc(str);
|
||||||
rb_str_check_dummy_enc(enc);
|
if (case_option_single_p(flags, enc, str)) {
|
||||||
if (((flags&ONIGENC_CASE_ASCII_ONLY) && (rb_enc_mbmaxlen(enc)==1))
|
if (upcase_single(str))
|
||||||
|| (!(flags&ONIGENC_CASE_FOLD_TURKISH_AZERI) && ENC_CODERANGE(str)==ENC_CODERANGE_7BIT)) {
|
flags |= ONIGENC_CASE_MODIFIED;
|
||||||
char *s = RSTRING_PTR(str), *send = RSTRING_END(str);
|
|
||||||
|
|
||||||
while (s < send) {
|
|
||||||
unsigned int c = *(unsigned char*)s;
|
|
||||||
|
|
||||||
if (rb_enc_isascii(c, enc) && 'a' <= c && c <= 'z') {
|
|
||||||
*s = 'A' + (c - 'a');
|
|
||||||
flags |= ONIGENC_CASE_MODIFIED;
|
|
||||||
}
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (flags&ONIGENC_CASE_ASCII_ONLY)
|
else if (flags&ONIGENC_CASE_ASCII_ONLY)
|
||||||
rb_str_ascii_casemap(str, &flags, enc);
|
rb_str_ascii_casemap(str, str, &flags, enc);
|
||||||
else
|
else
|
||||||
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
||||||
|
|
||||||
|
@ -6640,9 +6677,46 @@ rb_str_upcase_bang(int argc, VALUE *argv, VALUE str)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_str_upcase(int argc, VALUE *argv, VALUE str)
|
rb_str_upcase(int argc, VALUE *argv, VALUE str)
|
||||||
{
|
{
|
||||||
str = rb_str_dup(str);
|
rb_encoding *enc;
|
||||||
rb_str_upcase_bang(argc, argv, str);
|
OnigCaseFoldType flags = ONIGENC_CASE_UPCASE;
|
||||||
return str;
|
VALUE ret;
|
||||||
|
|
||||||
|
flags = check_case_options(argc, argv, flags);
|
||||||
|
enc = str_true_enc(str);
|
||||||
|
if (case_option_single_p(flags, enc, str)) {
|
||||||
|
ret = rb_str_new_with_class(str, RSTRING_PTR(str), RSTRING_LEN(str));
|
||||||
|
OBJ_INFECT_RAW(ret, str);
|
||||||
|
str_enc_copy(ret, str);
|
||||||
|
upcase_single(ret);
|
||||||
|
}
|
||||||
|
else if (flags&ONIGENC_CASE_ASCII_ONLY) {
|
||||||
|
ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str));
|
||||||
|
rb_str_ascii_casemap(str, ret, &flags, enc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = rb_str_casemap(str, &flags, enc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
downcase_single(VALUE str)
|
||||||
|
{
|
||||||
|
char *s = RSTRING_PTR(str), *send = RSTRING_END(str);
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
|
while (s < send) {
|
||||||
|
unsigned int c = *(unsigned char*)s;
|
||||||
|
|
||||||
|
if (rb_enc_isascii(c, enc) && 'A' <= c && c <= 'Z') {
|
||||||
|
*s = 'a' + (c - 'A');
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -6664,24 +6738,13 @@ rb_str_downcase_bang(int argc, VALUE *argv, VALUE str)
|
||||||
|
|
||||||
flags = check_case_options(argc, argv, flags);
|
flags = check_case_options(argc, argv, flags);
|
||||||
str_modify_keep_cr(str);
|
str_modify_keep_cr(str);
|
||||||
enc = STR_ENC_GET(str);
|
enc = str_true_enc(str);
|
||||||
rb_str_check_dummy_enc(enc);
|
if (case_option_single_p(flags, enc, str)) {
|
||||||
if (((flags&ONIGENC_CASE_ASCII_ONLY) && (rb_enc_mbmaxlen(enc)==1))
|
if (downcase_single(str))
|
||||||
|| (!(flags&ONIGENC_CASE_FOLD_TURKISH_AZERI) && ENC_CODERANGE(str)==ENC_CODERANGE_7BIT)) {
|
flags |= ONIGENC_CASE_MODIFIED;
|
||||||
char *s = RSTRING_PTR(str), *send = RSTRING_END(str);
|
|
||||||
|
|
||||||
while (s < send) {
|
|
||||||
unsigned int c = *(unsigned char*)s;
|
|
||||||
|
|
||||||
if (rb_enc_isascii(c, enc) && 'A' <= c && c <= 'Z') {
|
|
||||||
*s = 'a' + (c - 'A');
|
|
||||||
flags |= ONIGENC_CASE_MODIFIED;
|
|
||||||
}
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (flags&ONIGENC_CASE_ASCII_ONLY)
|
else if (flags&ONIGENC_CASE_ASCII_ONLY)
|
||||||
rb_str_ascii_casemap(str, &flags, enc);
|
rb_str_ascii_casemap(str, str, &flags, enc);
|
||||||
else
|
else
|
||||||
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
||||||
|
|
||||||
|
@ -6743,9 +6806,27 @@ rb_str_downcase_bang(int argc, VALUE *argv, VALUE str)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_str_downcase(int argc, VALUE *argv, VALUE str)
|
rb_str_downcase(int argc, VALUE *argv, VALUE str)
|
||||||
{
|
{
|
||||||
str = rb_str_dup(str);
|
rb_encoding *enc;
|
||||||
rb_str_downcase_bang(argc, argv, str);
|
OnigCaseFoldType flags = ONIGENC_CASE_DOWNCASE;
|
||||||
return str;
|
VALUE ret;
|
||||||
|
|
||||||
|
flags = check_case_options(argc, argv, flags);
|
||||||
|
enc = str_true_enc(str);
|
||||||
|
if (case_option_single_p(flags, enc, str)) {
|
||||||
|
ret = rb_str_new_with_class(str, RSTRING_PTR(str), RSTRING_LEN(str));
|
||||||
|
OBJ_INFECT_RAW(ret, str);
|
||||||
|
str_enc_copy(ret, str);
|
||||||
|
downcase_single(ret);
|
||||||
|
}
|
||||||
|
else if (flags&ONIGENC_CASE_ASCII_ONLY) {
|
||||||
|
ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str));
|
||||||
|
rb_str_ascii_casemap(str, ret, &flags, enc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = rb_str_casemap(str, &flags, enc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6775,11 +6856,10 @@ rb_str_capitalize_bang(int argc, VALUE *argv, VALUE str)
|
||||||
|
|
||||||
flags = check_case_options(argc, argv, flags);
|
flags = check_case_options(argc, argv, flags);
|
||||||
str_modify_keep_cr(str);
|
str_modify_keep_cr(str);
|
||||||
enc = STR_ENC_GET(str);
|
enc = str_true_enc(str);
|
||||||
rb_str_check_dummy_enc(enc);
|
|
||||||
if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return Qnil;
|
if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return Qnil;
|
||||||
if (flags&ONIGENC_CASE_ASCII_ONLY)
|
if (flags&ONIGENC_CASE_ASCII_ONLY)
|
||||||
rb_str_ascii_casemap(str, &flags, enc);
|
rb_str_ascii_casemap(str, str, &flags, enc);
|
||||||
else
|
else
|
||||||
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
||||||
|
|
||||||
|
@ -6806,9 +6886,21 @@ rb_str_capitalize_bang(int argc, VALUE *argv, VALUE str)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_str_capitalize(int argc, VALUE *argv, VALUE str)
|
rb_str_capitalize(int argc, VALUE *argv, VALUE str)
|
||||||
{
|
{
|
||||||
str = rb_str_dup(str);
|
rb_encoding *enc;
|
||||||
rb_str_capitalize_bang(argc, argv, str);
|
OnigCaseFoldType flags = ONIGENC_CASE_UPCASE | ONIGENC_CASE_TITLECASE;
|
||||||
return str;
|
VALUE ret;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str));
|
||||||
|
rb_str_ascii_casemap(str, ret, &flags, enc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = rb_str_casemap(str, &flags, enc);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6832,10 +6924,9 @@ rb_str_swapcase_bang(int argc, VALUE *argv, VALUE str)
|
||||||
|
|
||||||
flags = check_case_options(argc, argv, flags);
|
flags = check_case_options(argc, argv, flags);
|
||||||
str_modify_keep_cr(str);
|
str_modify_keep_cr(str);
|
||||||
enc = STR_ENC_GET(str);
|
enc = str_true_enc(str);
|
||||||
rb_str_check_dummy_enc(enc);
|
|
||||||
if (flags&ONIGENC_CASE_ASCII_ONLY)
|
if (flags&ONIGENC_CASE_ASCII_ONLY)
|
||||||
rb_str_ascii_casemap(str, &flags, enc);
|
rb_str_ascii_casemap(str, str, &flags, enc);
|
||||||
else
|
else
|
||||||
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
str_shared_replace(str, rb_str_casemap(str, &flags, enc));
|
||||||
|
|
||||||
|
@ -6861,9 +6952,21 @@ rb_str_swapcase_bang(int argc, VALUE *argv, VALUE str)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_str_swapcase(int argc, VALUE *argv, VALUE str)
|
rb_str_swapcase(int argc, VALUE *argv, VALUE str)
|
||||||
{
|
{
|
||||||
str = rb_str_dup(str);
|
rb_encoding *enc;
|
||||||
rb_str_swapcase_bang(argc, argv, str);
|
OnigCaseFoldType flags = ONIGENC_CASE_UPCASE | ONIGENC_CASE_DOWNCASE;
|
||||||
return str;
|
VALUE ret;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str));
|
||||||
|
rb_str_ascii_casemap(str, ret, &flags, enc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = rb_str_casemap(str, &flags, enc);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef unsigned char *USTR;
|
typedef unsigned char *USTR;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue