From 802fa3e44b8ed88d0a3ce873a030a4beb18add16 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:32:16 +0000 Subject: [PATCH] feat: add fast-path to for_each_char_match_start Extracts first-character ASCII conversions out of the search hot loop to prevent redundant iterations when checking common prefixes. Reduces baseline iteration overhead during full-scrollback searches. Co-authored-by: Lucenx9 <185146821+Lucenx9@users.noreply.github.com> --- .jules/bolt.md | 3 +++ .../forktty-ui-gtk/src/gtk_app/terminal_search.rs | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..98628f6d --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-18 - [Terminal Search Fast-path Optimization] +**Learning:** Hoisting the first-character case-conversion out of the search hot loop `for_each_char_match_start` significantly reduces overhead. +**Action:** In search loops, perform the expensive `.to_ascii_lowercase()`/`.to_ascii_uppercase()` once before the loop, and use it inside the fast path. diff --git a/crates/forktty-ui-gtk/src/gtk_app/terminal_search.rs b/crates/forktty-ui-gtk/src/gtk_app/terminal_search.rs index 90694aa2..8a115106 100644 --- a/crates/forktty-ui-gtk/src/gtk_app/terminal_search.rs +++ b/crates/forktty-ui-gtk/src/gtk_app/terminal_search.rs @@ -75,11 +75,21 @@ fn for_each_char_match_start( return; } let first_needle = needle[0]; + let first_lower = first_needle.to_ascii_lowercase(); + let first_upper = first_needle.to_ascii_uppercase(); + let mut index = 0; while index + needle.len() <= haystack.len() { + let h = haystack[index]; // Fast-path: short-circuit the full substring check if the first character - // doesn't match, avoiding iterator overhead in the common case. - if !chars_eq_ignore_case(haystack[index], first_needle) { + // doesn't match. We can only short-circuit if BOTH the haystack character + // AND the needle character are ASCII. Non-ASCII characters might case-fold + // to ASCII (e.g., Kelvin sign U+212A folds to 'k'). + if h != first_lower && h != first_upper && h.is_ascii() && first_needle.is_ascii() { + index += 1; + continue; + } + if !chars_eq_ignore_case(h, first_needle) { index += 1; continue; }