perf(console): case-insensitive filtering without per-line copies

Console text filtering and match highlighting lowercased a full copy of
every line, and of every drawn segment, on each pass.

- A new alloc-free ASCII case-insensitive substring search folds both
  sides on the fly, so neither the filter test nor the highlight scan
  allocates a lowercased copy per line any more.
- Both call sites use it; the case-insensitive match behaviour is
  unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 08:23:30 -05:00
co-authored by Claude Opus 4.8
parent 6a24355c79
commit 70e96f75f0
3 changed files with 18 additions and 13 deletions
+12 -9
View File
@@ -6,13 +6,18 @@
namespace dragonx {
namespace ui {
namespace {
std::string lowerCopy(std::string value)
size_t FindCaseInsensitive(const char* hay, size_t hayLen, const std::string& needleLower, size_t from)
{
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return value;
}
const size_t n = needleLower.size();
if (n == 0) return from <= hayLen ? from : std::string::npos;
if (hayLen < n) return std::string::npos;
auto fold = [](unsigned char c) { return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c; };
for (size_t i = from; i + n <= hayLen; ++i) {
size_t j = 0;
while (j < n && fold(static_cast<unsigned char>(hay[i + j])) == fold(static_cast<unsigned char>(needleLower[j]))) ++j;
if (j == n) return i;
}
return std::string::npos;
}
bool consoleLinePassesFilter(const std::string& lineText,
@@ -25,9 +30,7 @@ bool consoleLinePassesFilter(const std::string& lineText,
if (!filter.appMessagesEnabled && channel == ConsoleChannel::App) return false;
if (filter.errorsOnly && channel != ConsoleChannel::Error) return false;
if (!filter.text.empty()) {
std::string needle = lowerCopy(filter.text);
std::string haystack = lowerCopy(lineText);
if (haystack.find(needle) == std::string::npos) return false;
if (FindCaseInsensitive(lineText.data(), lineText.size(), filter.text, 0) == std::string::npos) return false;
}
return true;
}