From 65fdf15667d720e6570d551708f0af377137ab58 Mon Sep 17 00:00:00 2001 From: LeanBitLab <245915690+LeanBitLab@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:09:00 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20regex=20pattern?= =?UTF-8?q?=20detection=20in=20DictionaryFacilitatorImpl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced chained String.contains() and startsWith() calls with a single manual character iteration loop in DictionaryFacilitatorImpl.isRegexPattern. This reduces method call overhead and CPU time on the hot path. --- .jules/bolt.md | 3 +++ .../latin/DictionaryFacilitatorImpl.kt | 25 ++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index f778fe2d9..8513f7ad5 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -24,3 +24,6 @@ ## 2024-05-18 - Optimize Diacritic Stripping in Suggest **Learning:** In `Suggest.isSameIgnoringAccents`, `val DIACRITIC_REGEX = "\\p{M}+".toRegex()` was being used in combination with `Normalizer.normalize` and `replace` to strip accents. This created string allocations via `Normalizer.normalize` and `Regex.replace` even for plain ASCII strings that lack accents, which is a hot path during typing and suggestion processing. **Action:** Replace `Regex.replace` with a manual character loop that avoids `Normalizer.normalize` entirely for pure ASCII strings and strips `Character.NON_SPACING_MARK` without regex allocation when normalizing. +## 2024-05-18 - Optimize Regex pattern detection in DictionaryFacilitatorImpl +**Learning:** `DictionaryFacilitatorImpl.isRegexPattern` used chained `String.contains()` and `String.startsWith()` calls, which internally execute full loops over the string (or delegate to regex). Replacing these chained calls with a single manual O(N) iteration reduces overhead and prevents repeatedly traversing the string. +**Action:** Replace chained string detection methods (`contains`, `startsWith`, `endsWith`) with a single manual loop over the string's indices when checking multiple characters in performance hot paths. diff --git a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt index 879cb8eb8..96191c15d 100644 --- a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt +++ b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.kt @@ -1235,15 +1235,22 @@ private class DictionaryGroup( private var compiledBlacklistPatterns: List = emptyList() private fun isRegexPattern(pattern: String): Boolean { - return pattern.contains(".*") - || pattern.contains(".+") - || pattern.startsWith("^") - || pattern.endsWith("$") - || pattern.contains("[") - || pattern.contains("(") - || pattern.contains("\\") - || pattern.contains("|") - || pattern.contains("{") + if (pattern.isEmpty()) return false + val len = pattern.length + + // ⚡ Bolt: Optimize regex pattern detection + // Replaced chained String.contains() and startsWith() calls with a single + // manual character iteration loop to reduce method overhead and execution time. + if (pattern[0] == '^' || pattern[len - 1] == '$') return true + for (i in 0 until len) { + val c = pattern[i] + if (c == '[' || c == '(' || c == '\\' || c == '|' || c == '{') return true + if (c == '.' && i + 1 < len) { + val next = pattern[i + 1] + if (next == '*' || next == '+') return true + } + } + return false } private fun rebuildCompiledPatterns() {