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
+2 -4
View File
@@ -1196,11 +1196,9 @@ void ConsoleTab::drawVisibleLines(float padX, float lineHeight, bool hasTextFilt
// Filter match highlight (case-insensitive) within this segment.
if (hasTextFilter && !filterLower.empty() && seg.byteStart < seg.byteEnd) {
std::string segLower(segStart, segEnd);
std::transform(segLower.begin(), segLower.end(), segLower.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
const size_t segBytes = static_cast<size_t>(segEnd - segStart);
size_t pos = 0;
while ((pos = segLower.find(filterLower, pos)) != std::string::npos) {
while ((pos = FindCaseInsensitive(segStart, segBytes, filterLower, pos)) != std::string::npos) {
float xs = font->CalcTextSizeA(fontSize, FLT_MAX, 0, segStart, segStart + static_cast<int>(pos)).x;
float xe = font->CalcTextSizeA(fontSize, FLT_MAX, 0, segStart,
segStart + static_cast<int>(pos + filterLower.size())).x;