diff options
| author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2025-04-15 06:25:54 +0300 |
|---|---|---|
| committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2025-04-15 06:25:54 +0300 |
| commit | e9d6b27c381cdf0cc9406238bff1224b78c49cf5 (patch) | |
| tree | 887d43a7e3f74de095f89bf55ca02af75baa74c1 | |
| parent | 403fcb204381fd7a3d40f9924bfad498c3a17b6a (diff) | |
Optimize LLKeywords: pre-compile regex patterns
| -rw-r--r-- | indra/llui/llkeywords.cpp | 164 | ||||
| -rw-r--r-- | indra/llui/llkeywords.h | 16 |
2 files changed, 110 insertions, 70 deletions
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 85f57b6548..19376ad8fa 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -282,6 +282,21 @@ void LLKeywords::processTokens() } } } + + // Pre-compile all regex patterns for tokens in mRegexTokenList + for (LLKeywordToken* regex_token : mRegexTokenList) + { + std::string start_pattern(regex_token->getToken().begin(), regex_token->getToken().end()); + try + { + regex_token->setCompiledRegex(new std::regex(start_pattern)); + } + catch (const std::regex_error& e) + { + LL_WARNS() << "Regex error in start pattern: " << e.what() << " in pattern: " << start_pattern << LL_ENDL; + } + } + LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL; } @@ -599,99 +614,110 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW for (LLKeywordToken* regex_token : mRegexTokenList) { - std::string start_pattern(regex_token->getToken().begin(), regex_token->getToken().end()); - std::string end_pattern(regex_token->getDelimiter().begin(), regex_token->getDelimiter().end()); + std::regex* compiled_regex = regex_token->getCompiledRegex(); - try + // If we have a pre-compiled regex, use it + if (compiled_regex) { - std::regex start_regex_pattern(start_pattern); - std::smatch start_match; + std::string end_pattern(regex_token->getDelimiter().begin(), regex_token->getDelimiter().end()); - if (std::regex_search(text_to_search, start_match, start_regex_pattern) && !start_match.empty()) + try { - if (start_match.position() == 0) // Match starts at current position - { - // Calculate segment boundaries for start pattern - S32 start_match_length = static_cast<S32>(start_match.str().length()); - S32 start_seg_end = seg_start + start_match_length; - - if (end_pattern.empty()) - { - // If no end pattern is provided, treat the entire regex match as a single segment - // Move cursor past the matched segment - cur = base + start_seg_end; + std::smatch start_match; - // Insert the matched segment - insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, start_seg_end, style, editor); - } - else + if (std::regex_search(text_to_search, start_match, *compiled_regex) && !start_match.empty()) + { + if (start_match.position() == 0) // Match starts at current position { - // Look for the end pattern after the start pattern - std::string remaining_text = text_to_search.substr(start_match_length); + // Calculate segment boundaries for start pattern + S32 start_match_length = static_cast<S32>(start_match.str().length()); + S32 start_seg_end = seg_start + start_match_length; - // Process end pattern - replace any capture group references - std::string actual_end_pattern = end_pattern; - - // Handle capture groups in the end pattern (replace \1, \2, etc. with their matched content) - for (size_t i = 1; i < start_match.size(); ++i) + if (end_pattern.empty()) { - std::string capture = start_match[i].str(); - std::string placeholder = "\\" + std::to_string(i); + // If no end pattern is provided, treat the entire regex match as a single segment + // Move cursor past the matched segment + cur = base + start_seg_end; - // Replace all occurrences of the placeholder with the captured content - size_t pos = 0; - while ((pos = actual_end_pattern.find(placeholder, pos)) != std::string::npos) - { - actual_end_pattern.replace(pos, placeholder.length(), capture); - pos += capture.length(); - } + // Insert the matched segment + insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, start_seg_end, style, editor); } + else + { // TODO: better optimization for this part - try - { - std::regex end_regex_pattern(actual_end_pattern); - std::smatch end_match; + // Look for the end pattern after the start pattern + std::string remaining_text = text_to_search.substr(start_match_length); - S32 seg_end = start_seg_end; + // Process end pattern - replace any capture group references + std::string actual_end_pattern = end_pattern; - if (std::regex_search(remaining_text, end_match, end_regex_pattern) && !end_match.empty()) + // Handle capture groups in the end pattern (replace \1, \2, etc. with their matched content) + for (size_t i = 1; i < start_match.size(); ++i) { - // Calculate position of end match relative to the original text - S32 end_match_position = static_cast<S32>(end_match.position()); - S32 end_match_length = static_cast<S32>(end_match.str().length()); + std::string capture = start_match[i].str(); + std::string placeholder = "\\" + std::to_string(i); - // Calculate the total length including both patterns and text between - seg_end += end_match_position + end_match_length; + // Replace all occurrences of the placeholder with the captured content + size_t pos = 0; + while ((pos = actual_end_pattern.find(placeholder, pos)) != std::string::npos) + { + actual_end_pattern.replace(pos, placeholder.length(), capture); + pos += capture.length(); + } } - else + + try { - // End pattern not found, treat everything up to EOF as the segment - seg_end += static_cast<S32>(remaining_text.length()); - } + std::regex end_regex_pattern(actual_end_pattern); + std::smatch end_match; - // Move cursor past the entire matched segment (start + content + end) - cur = base + seg_end; + S32 seg_end = start_seg_end; - // Insert the matched segment - insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, seg_end, style, editor); - } - catch (const std::regex_error& e) - { - LL_WARNS() << "Regex error in end pattern: " << e.what() << " in pattern: " << actual_end_pattern << LL_ENDL; - // Fall back to treating the start match as the entire segment - cur = base + start_seg_end; - insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, start_seg_end, style, editor); + if (std::regex_search(remaining_text, end_match, end_regex_pattern) && !end_match.empty()) + { + // Calculate position of end match relative to the original text + S32 end_match_position = static_cast<S32>(end_match.position()); + S32 end_match_length = static_cast<S32>(end_match.str().length()); + + // Calculate the total length including both patterns and text between + seg_end += end_match_position + end_match_length; + } + else + { + // End pattern not found, treat everything up to EOF as the segment + seg_end += static_cast<S32>(remaining_text.length()); + } + + // Move cursor past the entire matched segment (start + content + end) + cur = base + seg_end; + + // Insert the matched segment + insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, seg_end, style, editor); + } + catch (const std::regex_error& e) + { + LL_WARNS() << "Regex error in end pattern: " << e.what() << " in pattern: " << actual_end_pattern << LL_ENDL; + // Fall back to treating the start match as the entire segment + cur = base + start_seg_end; + insertSegments(wtext, *seg_list, regex_token, text_len, seg_start, start_seg_end, style, editor); + } } - } - regex_matched = true; - break; + regex_matched = true; + break; + } } } + catch (const std::regex_error& e) + { + LL_WARNS() << "Error using compiled regex: " << e.what() << LL_ENDL; + } } - catch (const std::regex_error& e) + else { - LL_WARNS() << "Regex error in start pattern: " << e.what() << " in pattern: " << start_pattern << LL_ENDL; + // Skip tokens that aren't pre-compiled + LL_WARNS() << "Skipping regex token due to missing pre-compiled pattern: " + << wstring_to_utf8str(regex_token->getToken()) << LL_ENDL; } } @@ -701,7 +727,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW } } - // If no regex match, check against delimiters + // Check against delimiters { S32 seg_start = 0; LLKeywordToken* cur_delimiter = NULL; diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h index 853ba0fe81..ad8310fa4a 100644 --- a/indra/llui/llkeywords.h +++ b/indra/llui/llkeywords.h @@ -36,6 +36,7 @@ #include <map> #include <list> #include <deque> +#include <regex> #include "llpointer.h" class LLTextSegment; @@ -84,10 +85,20 @@ public: mToken( token ), mColor( color ), mToolTip( tool_tip ), - mDelimiter( delimiter ) // right delimiter + mDelimiter( delimiter ), // right delimiter + mCompiledRegex( nullptr ) { } + ~LLKeywordToken() + { + if (mCompiledRegex) + { + delete mCompiledRegex; + mCompiledRegex = nullptr; + } + } + S32 getLengthHead() const { return static_cast<S32>(mToken.size()); } S32 getLengthTail() const { return static_cast<S32>(mDelimiter.size()); } bool isHead(const llwchar* s) const; @@ -97,6 +108,8 @@ public: ETokenType getType() const { return mType; } const LLWString& getToolTip() const { return mToolTip; } const LLWString& getDelimiter() const { return mDelimiter; } + std::regex* getCompiledRegex() const { return mCompiledRegex; } + void setCompiledRegex(std::regex* regex) { mCompiledRegex = regex; } #ifdef _DEBUG void dump(); @@ -108,6 +121,7 @@ private: LLUIColor mColor; LLWString mToolTip; LLWString mDelimiter; + std::regex* mCompiledRegex; }; class LLKeywords |
