diff --git a/.jules/bolt.md b/.jules/bolt.md index c822df0bb..d783bb454 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -8,3 +8,7 @@ ## 2024-05-18 - Pre-compile Regex in hot dictionary loops **Learning:** Instantiating `Regex` objects inside dictionary processing loops (like adding words in `UserBinaryDictionary` or `AppsBinaryDictionary`) causes unnecessary allocations and compilation overhead on every item iteration. **Action:** Always pre-compile `Regex` objects as `private val` properties in the class or companion object to eliminate per-iteration compilation and allocation pressure. + +## 2024-05-18 - Pre-compile Regex in model importers +**Learning:** `TranslationModelImporter` and `HandwritingModelImporter` create `Regex` instances inside `detectLanguageCode` and `detectLanguageTag` methods, sometimes inside a loop parsing zip entries. This causes unnecessary overhead on every detection call. +**Action:** Extract frequently used `Regex` instances into `companion object`s as `private val`s to ensure they are compiled only once, reducing execution time and object allocation. diff --git a/app/src/main/java/helium314/keyboard/latin/handwriting/HandwritingModelImporter.kt b/app/src/main/java/helium314/keyboard/latin/handwriting/HandwritingModelImporter.kt index 3fd8e9b70..b7a40d013 100644 --- a/app/src/main/java/helium314/keyboard/latin/handwriting/HandwritingModelImporter.kt +++ b/app/src/main/java/helium314/keyboard/latin/handwriting/HandwritingModelImporter.kt @@ -170,19 +170,20 @@ object HandwritingModelImporter { } } + private val QRNN_REGEX = Regex("""qrnn[._]([a-z]{2,3}(?:[_-][a-z0-9]+)?)[._]reco""") + private val FST_REGEX = Regex("""^([a-z]{2,3}(?:[_-][a-z0-9]+)?)[._]\d+[._]compact""") + private val ZIP_REGEX = Regex("""^([a-z]{2,3}(?:[_-][a-z0-9]+)?)(?:[._-]model)?\.zip$""") + private val LSTM_REGEX = Regex("""lstm[._]([a-z]+)[._]""") + fun detectLanguageTag(filename: String): String? { val name = filename.lowercase() - val qrnnRegex = Regex("""qrnn[._]([a-z]{2,3}(?:[_-][a-z0-9]+)?)[._]reco""") - qrnnRegex.find(name)?.let { return formatBcp47(it.groupValues[1]) } + QRNN_REGEX.find(name)?.let { return formatBcp47(it.groupValues[1]) } - val fstRegex = Regex("""^([a-z]{2,3}(?:[_-][a-z0-9]+)?)[._]\d+[._]compact""") - fstRegex.find(name)?.let { return formatBcp47(it.groupValues[1]) } + FST_REGEX.find(name)?.let { return formatBcp47(it.groupValues[1]) } - val zipRegex = Regex("""^([a-z]{2,3}(?:[_-][a-z0-9]+)?)(?:[._-]model)?\.zip$""") - zipRegex.find(name)?.let { return formatBcp47(it.groupValues[1]) } + ZIP_REGEX.find(name)?.let { return formatBcp47(it.groupValues[1]) } - val lstmRegex = Regex("""lstm[._]([a-z]+)[._]""") - lstmRegex.find(name)?.let { + LSTM_REGEX.find(name)?.let { val script = it.groupValues[1] val lang = SCRIPT_TO_LANG[script] ?: script return formatBcp47(lang) diff --git a/app/src/main/java/helium314/keyboard/latin/translation/TranslationModelImporter.kt b/app/src/main/java/helium314/keyboard/latin/translation/TranslationModelImporter.kt index a87418538..81b308f13 100644 --- a/app/src/main/java/helium314/keyboard/latin/translation/TranslationModelImporter.kt +++ b/app/src/main/java/helium314/keyboard/latin/translation/TranslationModelImporter.kt @@ -158,10 +158,15 @@ object TranslationModelImporter { return uri.lastPathSegment } + private val FN_MATCH_REGEX = Regex("""(?:dict\.|merged_dict_)?([a-z]{2,3})[_-]([a-z]{2,3})""") + private val SINGLE_MATCH_REGEX = Regex("""^([a-z]{2,3})(?:[._-]model)?\.zip$""") + private val INNER_MATCH_REGEX = Regex("""(?:dict\.|merged_dict_)([a-z]{2,3})_([a-z]{2,3})""") + private val SINGLE_INNER_MATCH_REGEX = Regex("""(?:dict\.|merged_dict_)([a-z]{2,3}_[a-z]{2,3})""") + fun detectLanguageCode(context: Context, uri: Uri): String? { val filename = getFilename(context, uri) ?: uri.lastPathSegment ?: "" val fnLower = filename.lowercase() - val fnMatch = Regex("""(?:dict\.|merged_dict_)?([a-z]{2,3})[_-]([a-z]{2,3})""").find(fnLower) + val fnMatch = FN_MATCH_REGEX.find(fnLower) if (fnMatch != null) { val code1 = fnMatch.groupValues[1] val code2 = fnMatch.groupValues[2] @@ -169,7 +174,7 @@ object TranslationModelImporter { if (code2 == "en") return code1 return code1 } - val singleMatch = Regex("""^([a-z]{2,3})(?:[._-]model)?\.zip$""").find(fnLower) + val singleMatch = SINGLE_MATCH_REGEX.find(fnLower) if (singleMatch != null) { return singleMatch.groupValues[1] } @@ -180,7 +185,7 @@ object TranslationModelImporter { var entry = zipIn.nextEntry while (entry != null) { val name = entry.name.lowercase() - val match = Regex("""(?:dict\.|merged_dict_)([a-z]{2,3})_([a-z]{2,3})""").find(name) + val match = INNER_MATCH_REGEX.find(name) if (match != null) { val code1 = match.groupValues[1] val code2 = match.groupValues[2] @@ -295,7 +300,7 @@ object TranslationModelImporter { while (entries.hasMoreElements()) { val entry = entries.nextElement() val name = entry.name - val match = Regex("""(?:dict\.|merged_dict_)([a-z]{2,3}_[a-z]{2,3})""").find(name) + val match = SINGLE_INNER_MATCH_REGEX.find(name) if (match != null) { detectedModelName = match.groupValues[1] break