Add base::FindFirstNonASCII [chromium/src : main]

0 views
Skip to first unread message

Anthony Vallee-Dubois (Gerrit)

unread,
Jul 17, 2026, 12:54:19 PM (4 days ago) Jul 17
to Greg Thompson, Chromium LUCI CQ, chromium...@chromium.org, jshin...@chromium.org
Attention needed from Greg Thompson

Anthony Vallee-Dubois added 1 comment

Patchset-level comments
File-level comment, Patchset 4 (Latest):
Anthony Vallee-Dubois . resolved

Hi Greg, PTAL!

I tried to keep this function as similar to `DoIsStringASCII` as possible to make it easier to review since it contains a bunch of unchecked pointer manipulation (for performance). Open to suggestions if you don't think that's the right way to go.

Thanks!

Open in Gerrit

Related details

Attention is currently required from:
  • Greg Thompson
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I83f606db3552fb4fffc75d133e131afaa86ab4ea
Gerrit-Change-Number: 8083987
Gerrit-PatchSet: 4
Gerrit-Owner: Anthony Vallee-Dubois <anth...@chromium.org>
Gerrit-Reviewer: Anthony Vallee-Dubois <anth...@chromium.org>
Gerrit-Reviewer: Greg Thompson <g...@chromium.org>
Gerrit-Attention: Greg Thompson <g...@chromium.org>
Gerrit-Comment-Date: Fri, 17 Jul 2026 16:54:08 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Greg Thompson (Gerrit)

unread,
Jul 20, 2026, 7:12:51 AM (yesterday) Jul 20
to Anthony Vallee-Dubois, Chromium LUCI CQ, chromium...@chromium.org, jshin...@chromium.org
Attention needed from Anthony Vallee-Dubois

Greg Thompson voted and added 4 comments

Votes added by Greg Thompson

Code-Review+1

4 comments

Commit Message
Line 27, Patchset 4 (Latest):codepaths and because it's slightly faster in come cases (when the use
Greg Thompson . unresolved

"some"

File base/strings/string_util_impl_helpers.h
Line 262, Patchset 4 (Latest): while (characters <= UNSAFE_TODO(end - chars_per_word)) {
Greg Thompson . unresolved

Please fix this WARNING reported by autoreview issue finding: NIT: Same here. Consider `while (static_cast<size_t>(end - characters) >= chars_per_word)` to avoid the potential pointer arithmetic UB.

Line 247, Patchset 4 (Latest): while (characters <= UNSAFE_TODO(end - batch_count * chars_per_word)) {
Greg Thompson . unresolved

Please fix this WARNING reported by autoreview issue finding: NIT: `end - batch_count * chars_per_word` calculates a pointer that could fall before the start of the array if the string is very short (`length < batch_count * chars_per_word`). Computing a pointer before the array bounds is undefined behavior in C++, even if it is never dereferenced.

While this is a preexisting pattern copied from `DoIsStringASCII` and likely works in practice, you can easily avoid the UB by checking the remaining length instead:
```cpp
while (static_cast<size_t>(end - characters) >= batch_count * chars_per_word) {
```

File base/strings/string_util_unittest.cc
Line 568, Patchset 4 (Latest): EXPECT_EQ(0u, FindFirstNonASCII(std::string_view("")));
Greg Thompson . unresolved

are these necessary? `std::string_view` can be implicitly constructed from a string literal.

Open in Gerrit

Related details

Attention is currently required from:
  • Anthony Vallee-Dubois
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement satisfiedCode-Owners
  • requirement satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I83f606db3552fb4fffc75d133e131afaa86ab4ea
Gerrit-Change-Number: 8083987
Gerrit-PatchSet: 4
Gerrit-Owner: Anthony Vallee-Dubois <anth...@chromium.org>
Gerrit-Reviewer: Anthony Vallee-Dubois <anth...@chromium.org>
Gerrit-Reviewer: Greg Thompson <g...@chromium.org>
Gerrit-Attention: Anthony Vallee-Dubois <anth...@chromium.org>
Gerrit-Comment-Date: Mon, 20 Jul 2026 11:12:34 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Anthony Vallee-Dubois (Gerrit)

unread,
Jul 20, 2026, 11:32:28 AM (yesterday) Jul 20
to Greg Thompson, Chromium LUCI CQ, chromium...@chromium.org, jshin...@chromium.org

Anthony Vallee-Dubois added 4 comments

Commit Message
Line 27, Patchset 4:codepaths and because it's slightly faster in come cases (when the use
Greg Thompson . resolved

"some"

Anthony Vallee-Dubois

Done

File base/strings/string_util_impl_helpers.h
Line 262, Patchset 4: while (characters <= UNSAFE_TODO(end - chars_per_word)) {
Greg Thompson . resolved

Please fix this WARNING reported by autoreview issue finding: NIT: Same here. Consider `while (static_cast<size_t>(end - characters) >= chars_per_word)` to avoid the potential pointer arithmetic UB.

Anthony Vallee-Dubois

Done

Line 247, Patchset 4: while (characters <= UNSAFE_TODO(end - batch_count * chars_per_word)) {
Greg Thompson . resolved

Please fix this WARNING reported by autoreview issue finding: NIT: `end - batch_count * chars_per_word` calculates a pointer that could fall before the start of the array if the string is very short (`length < batch_count * chars_per_word`). Computing a pointer before the array bounds is undefined behavior in C++, even if it is never dereferenced.

While this is a preexisting pattern copied from `DoIsStringASCII` and likely works in practice, you can easily avoid the UB by checking the remaining length instead:
```cpp
while (static_cast<size_t>(end - characters) >= batch_count * chars_per_word) {
```

Anthony Vallee-Dubois

Good catch! Done. I'm thinking of sending a separate CL for the same fix in IsStringASCII to avoid any potential future issue. Worth it?

File base/strings/string_util_unittest.cc
Line 568, Patchset 4: EXPECT_EQ(0u, FindFirstNonASCII(std::string_view("")));
Greg Thompson . resolved

are these necessary? `std::string_view` can be implicitly constructed from a string literal.

Anthony Vallee-Dubois

Good catch, removed.

Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I83f606db3552fb4fffc75d133e131afaa86ab4ea
    Gerrit-Change-Number: 8083987
    Gerrit-PatchSet: 6
    Gerrit-Owner: Anthony Vallee-Dubois <anth...@chromium.org>
    Gerrit-Reviewer: Anthony Vallee-Dubois <anth...@chromium.org>
    Gerrit-Reviewer: Greg Thompson <g...@chromium.org>
    Gerrit-Comment-Date: Mon, 20 Jul 2026 15:32:18 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Greg Thompson <g...@chromium.org>
    satisfied_requirement
    open
    diffy

    Anthony Vallee-Dubois (Gerrit)

    unread,
    Jul 20, 2026, 1:07:36 PM (yesterday) Jul 20
    to Greg Thompson, Chromium LUCI CQ, chromium...@chromium.org, jshin...@chromium.org

    Anthony Vallee-Dubois voted Commit-Queue+2

    Commit-Queue+2
    Open in Gerrit

    Related details

    Attention set is empty
    Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I83f606db3552fb4fffc75d133e131afaa86ab4ea
    Gerrit-Change-Number: 8083987
    Gerrit-PatchSet: 6
    Gerrit-Owner: Anthony Vallee-Dubois <anth...@chromium.org>
    Gerrit-Reviewer: Anthony Vallee-Dubois <anth...@chromium.org>
    Gerrit-Reviewer: Greg Thompson <g...@chromium.org>
    Gerrit-Comment-Date: Mon, 20 Jul 2026 17:07:28 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: Yes
    satisfied_requirement
    open
    diffy

    Chromium LUCI CQ (Gerrit)

    unread,
    Jul 20, 2026, 1:13:58 PM (yesterday) Jul 20
    to Anthony Vallee-Dubois, Greg Thompson, chromium...@chromium.org, jshin...@chromium.org

    Chromium LUCI CQ submitted the change with unreviewed changes

    Unreviewed changes

    4 is the latest approved patch-set.
    The change was submitted with unreviewed changes in the following files:

    ```
    The name of the file: base/strings/string_util_impl_helpers.h
    Insertions: 7, Deletions: 2.

    @@ -244,7 +244,10 @@
    // Compare the values of CPU word size.
    constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char);
    constexpr int batch_count = 16;
    - while (characters <= UNSAFE_TODO(end - batch_count * chars_per_word)) {
    + // Process batches until the remaining amount of characters is smaller than a
    + // full batch size.
    + while (static_cast<size_t>(end - characters) >=
    + batch_count * chars_per_word) {
    const Char* batch_start = characters;
    all_char_bits = 0;
    for (int i = 0; i < batch_count; ++i) {
    @@ -259,7 +262,9 @@
    // Process the remaining words.
    const Char* remainder_start = characters;
    all_char_bits = 0;
    - while (characters <= UNSAFE_TODO(end - chars_per_word)) {
    + // Loop until the remaining byte count is lower than the amount of bytes in a
    + // word.
    + while (static_cast<size_t>(end - characters) >= chars_per_word) {
    all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
    UNSAFE_TODO(characters += chars_per_word);
    }
    ```
    ```
    The name of the file: base/strings/string_util_unittest.cc
    Insertions: 12, Deletions: 12.

    @@ -565,26 +565,26 @@

    TEST(StringUtilTest, FindFirstNonASCII) {
    // Branch 1: Empty string
    - EXPECT_EQ(0u, FindFirstNonASCII(std::string_view("")));
    - EXPECT_EQ(0u, FindFirstNonASCII(std::u16string_view(u"")));
    + EXPECT_EQ(0u, FindFirstNonASCII(""));
    + EXPECT_EQ(0u, FindFirstNonASCII(u""));

    // Branch 2: All ASCII strings (returns view.length())
    - EXPECT_EQ(5u, FindFirstNonASCII(std::string_view("Hello")));
    - EXPECT_EQ(12u, FindFirstNonASCII(std::string_view("Hello World!")));
    - EXPECT_EQ(11u, FindFirstNonASCII(std::u16string_view(u"Hello World")));
    + EXPECT_EQ(5u, FindFirstNonASCII("Hello"));
    + EXPECT_EQ(12u, FindFirstNonASCII("Hello World!"));
    + EXPECT_EQ(11u, FindFirstNonASCII(u"Hello World"));

    std::string long_ascii(200, 'a');
    EXPECT_EQ(200u, FindFirstNonASCII(long_ascii));

    // Branch 3: Non-ASCII in remainder / trailing bytes (< 1 batch length)
    - EXPECT_EQ(0u, FindFirstNonASCII(std::string_view("\x80World")));
    - EXPECT_EQ(5u, FindFirstNonASCII(std::string_view("Hello\x80World")));
    - EXPECT_EQ(5u, FindFirstNonASCII(std::u16string_view(u"Hello\u0080World")));
    + EXPECT_EQ(0u, FindFirstNonASCII("\x80World"));
    + EXPECT_EQ(5u, FindFirstNonASCII("Hello\x80World"));
    + EXPECT_EQ(5u, FindFirstNonASCII(u"Hello\u0080World"));

    // Branch 4: Non-ASCII in alignment prologue
    // Align offset so pointer is not aligned to MachineWord boundary.
    std::string prologue_str = " \x80Hello";
    - EXPECT_EQ(0u, FindFirstNonASCII(std::string_view(prologue_str).substr(3)));
    + EXPECT_EQ(0u, FindFirstNonASCII(prologue_str.substr(3)));

    // Branch 5: Non-ASCII inside 16-word batch (length >= 128 bytes for char)
    std::string batch_str(200, 'a');
    @@ -606,9 +606,9 @@
    EXPECT_EQ(129u, FindFirstNonASCII(remainder_str));

    #if defined(WCHAR_T_IS_32_BIT)
    - EXPECT_EQ(0u, FindFirstNonASCII(std::wstring_view(L"")));
    - EXPECT_EQ(11u, FindFirstNonASCII(std::wstring_view(L"Hello World")));
    - EXPECT_EQ(5u, FindFirstNonASCII(std::wstring_view(L"Hello\x80World")));
    + EXPECT_EQ(0u, FindFirstNonASCII(L""));
    + EXPECT_EQ(11u, FindFirstNonASCII(L"Hello World"));
    + EXPECT_EQ(5u, FindFirstNonASCII(L"Hello\x80World"));
    #endif
    }

    ```

    Change information

    Commit message:
    Add base::FindFirstNonASCII

    This function is an almost copy of base::IsStringASCII, except it
    returns the index of the first non-ASCII codepoint in the source string.

    This function will be helpful to speed up UTF8 -> UTF16 conversions in
    base:: and blink::. When converting UTF8, this function can be used to
    identify the bounds of ASCII content present at the beginning of a
    string and perform a fast widening copy instead of passing this portion
    of the string to the actual decoding algorithm.

    UTF8 decoding of ResourceBundle strings in base:: is responsible for a
    large portion of CPU time at startup (see
    https://issues.chromium.org/issues/517185267), and decoding of text
    resources is relevant for a large part of resource loading (most of the
    web's content is transmitted as UTF8 and a large portion of that content
    is ASCII heavy).

    base::IsStringASCII could be replaced with FindFirstNonASCII(source) ==
    source.length() but is preserved both for eventual A/B testing of some
    codepaths and because it's slightly faster in some cases (when the use
    case is only about determining if the string is ASCII and the passed
    string contains non-ASCII characters).
    Bug: 517185267
    Change-Id: I83f606db3552fb4fffc75d133e131afaa86ab4ea
    Reviewed-by: Greg Thompson <g...@chromium.org>
    Commit-Queue: Anthony Vallee-Dubois <anth...@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#1664776}
    Files:
    • M base/strings/string_util.cc
    • M base/strings/string_util.h
    • M base/strings/string_util_impl_helpers.h
    • M base/strings/string_util_unittest.cc
    • M base/strings/string_util_win.cc
    • M base/strings/string_util_win.h
    Change size: M
    Delta: 6 files changed, 154 insertions(+), 0 deletions(-)
    Branch: refs/heads/main
    Submit Requirements:
    • requirement satisfiedCode-Review: +1 by Greg Thompson
    Open in Gerrit
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: merged
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I83f606db3552fb4fffc75d133e131afaa86ab4ea
    Gerrit-Change-Number: 8083987
    Gerrit-PatchSet: 7
    Gerrit-Owner: Anthony Vallee-Dubois <anth...@chromium.org>
    Gerrit-Reviewer: Anthony Vallee-Dubois <anth...@chromium.org>
    Gerrit-Reviewer: Chromium LUCI CQ <chromiu...@luci-project-accounts.iam.gserviceaccount.com>
    Gerrit-Reviewer: Greg Thompson <g...@chromium.org>
    open
    diffy
    satisfied_requirement
    Reply all
    Reply to author
    Forward
    0 new messages