summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Lihatskiy <alihatskiy@productengine.com>2025-04-15 06:25:54 +0300
committerAndrey Lihatskiy <alihatskiy@productengine.com>2025-04-15 06:25:54 +0300
commite9d6b27c381cdf0cc9406238bff1224b78c49cf5 (patch)
tree887d43a7e3f74de095f89bf55ca02af75baa74c1
parent403fcb204381fd7a3d40f9924bfad498c3a17b6a (diff)
Optimize LLKeywords: pre-compile regex patterns
-rw-r--r--indra/llui/llkeywords.cpp164
-rw-r--r--indra/llui/llkeywords.h16
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