From 056ae1f9263fd78d8a087a3744d868b52d957651 Mon Sep 17 00:00:00 2001 From: FletcherDares <52580867+FletcherDares@users.noreply.github.com> Date: Tue, 2 Jun 2026 00:45:55 -0400 Subject: [PATCH] Centralize `ISASCII` early exit for `search_nonascii` (#17166) --- benchmark/string_coderange_scan.yml | 10 ++++ string.c | 74 +++++++++++++---------------- 2 files changed, 42 insertions(+), 42 deletions(-) create mode 100644 benchmark/string_coderange_scan.yml diff --git a/benchmark/string_coderange_scan.yml b/benchmark/string_coderange_scan.yml new file mode 100644 index 00000000000..d47bbd2b303 --- /dev/null +++ b/benchmark/string_coderange_scan.yml @@ -0,0 +1,10 @@ +prelude: | + def unknown(s) = s.b.force_encoding("UTF-8") + multibyte = unknown("\u{00e9}" * 16384) # best case: every byte non-ASCII + alternating = unknown("\u{00e9}a" * 10922) # worst case: non-ASCII then ASCII + ascii = unknown("a" * 32768) # baseline + +benchmark: + coderange_multibyte: multibyte.dup.valid_encoding? + coderange_alternating: alternating.dup.valid_encoding? + coderange_ascii: ascii.dup.valid_encoding? diff --git a/string.c b/string.c index 61d4f16679d..134e1254318 100644 --- a/string.c +++ b/string.c @@ -713,6 +713,10 @@ search_nonascii(const char *p, const char *e) { const char *s, *t; + if (p < e && !ISASCII(*p)) { + return p; + } + #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # if SIZEOF_UINTPTR_T == 8 # define NONASCII_MASK UINT64_C(0x8080808080808080) @@ -2297,26 +2301,22 @@ enc_strlen(const char *p, const char *e, rb_encoding *enc, int cr) c = 0; if (ENC_CODERANGE_CLEAN_P(cr)) { while (p < e) { - if (ISASCII(*p)) { - q = search_nonascii(p, e); - if (!q) - return c + (e - p); - c += q - p; - p = q; - } + q = search_nonascii(p, e); + if (!q) + return c + (e - p); + c += q - p; + p = q; p += rb_enc_fast_mbclen(p, e, enc); c++; } } else { while (p < e) { - if (ISASCII(*p)) { - q = search_nonascii(p, e); - if (!q) - return c + (e - p); - c += q - p; - p = q; - } + q = search_nonascii(p, e); + if (!q) + return c + (e - p); + c += q - p; + p = q; p += rb_enc_mbclen(p, e, enc); c++; } @@ -2354,15 +2354,13 @@ rb_enc_strlen_cr(const char *p, const char *e, rb_encoding *enc, int *cr) else if (rb_enc_asciicompat(enc)) { c = 0; while (p < e) { - if (ISASCII(*p)) { - q = search_nonascii(p, e); - if (!q) { - if (!*cr) *cr = ENC_CODERANGE_7BIT; - return c + (e - p); - } - c += q - p; - p = q; + q = search_nonascii(p, e); + if (!q) { + if (!*cr) *cr = ENC_CODERANGE_7BIT; + return c + (e - p); } + c += q - p; + p = q; ret = rb_enc_precise_mbclen(p, e, enc); if (MBCLEN_CHARFOUND_P(ret)) { *cr |= ENC_CODERANGE_VALID; @@ -3020,16 +3018,14 @@ str_nth_len(const char *p, const char *e, long *nthp, rb_encoding *enc) *nthp = nth; return (char *)e; } - if (ISASCII(*p)) { - p2 = search_nonascii(p, e2); - if (!p2) { - nth -= e2 - p; - *nthp = nth; - return (char *)e2; - } - nth -= p2 - p; - p = p2; + p2 = search_nonascii(p, e2); + if (!p2) { + nth -= e2 - p; + *nthp = nth; + return (char *)e2; } + nth -= p2 - p; + p = p2; n = rb_enc_mbclen(p, e, enc); p += n; nth--; @@ -11850,17 +11846,11 @@ enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl, int cr) else if (MBCLEN_CHARFOUND_P(ret)) { cr = ENC_CODERANGE_VALID; p += MBCLEN_CHARFOUND_LEN(ret); - /* - * After a valid multibyte character, skip the following ASCII run. - * If the next byte is already non-ASCII, search_nonascii would only - * rediscover p after its word-at-a-time setup. - */ - if (p < e && ISASCII(*p)) { - p = search_nonascii(p, e); - if (!p) { - p = e; - break; - } + /* After a multibyte character, fast-skip the following ASCII run. */ + p = search_nonascii(p, e); + if (!p) { + p = e; + break; } } else if (MBCLEN_INVALID_P(ret)) {