Perf: update the line index in place; renderers read only visible rows
- PieceTable::Insert/Delete adjust a valid line index directly (shift later line starts, add or drop starts for inserted or deleted newlines) instead of invalidating it, which forced a byte-by-byte rescan of the whole buffer on the next line query. On a 38 MB, 1M-line buffer, insert + line query drops from 14.7 ms to 0.28 ms. - The terminal and ImGui renderers fetch only the rows they draw (Nrows + GetLineString) instead of Buffer::Rows(), which rebuilt a string for every line of the file on the first frame after each edit. Qt's line-count uses switch to Nrows(). ImGuiRenderer.cc was syntax-checked with g++ and clang++ against the vendored ImGui headers; the GUI targets could not be built here (no SDL2 or Qt6). Audit findings P1, P2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
+41
-4
@@ -1,4 +1,5 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
@@ -358,6 +359,42 @@ PieceTable::InvalidateLineIndex() const
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PieceTable::lineIndexOnInsert(std::size_t offset, const char *text, std::size_t len) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (line_index_dirty_)
|
||||
return;
|
||||
// Line starts after the insertion point move right; a start exactly at
|
||||
// the insertion point stays (the text joins that line).
|
||||
auto first_after = std::upper_bound(line_index_.begin(), line_index_.end(), offset);
|
||||
for (auto it = first_after; it != line_index_.end(); ++it)
|
||||
*it += len;
|
||||
std::vector<std::size_t> added;
|
||||
const char *end = text + len;
|
||||
for (const char *p = text; (p = static_cast<const char *>(std::memchr(p, '\n', end - p))) != nullptr; ++p)
|
||||
added.push_back(offset + static_cast<std::size_t>(p - text) + 1);
|
||||
if (!added.empty())
|
||||
line_index_.insert(first_after, added.begin(), added.end());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PieceTable::lineIndexOnDelete(std::size_t offset, std::size_t len) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (line_index_dirty_)
|
||||
return;
|
||||
// Starts in (offset, offset+len] followed a deleted newline: drop them.
|
||||
// Later starts move left.
|
||||
auto lo = std::upper_bound(line_index_.begin(), line_index_.end(), offset);
|
||||
auto hi = std::upper_bound(lo, line_index_.end(), offset + len);
|
||||
for (auto it = hi; it != line_index_.end(); ++it)
|
||||
*it -= len;
|
||||
line_index_.erase(lo, hi);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PieceTable::RebuildLineIndex() const
|
||||
{
|
||||
@@ -405,7 +442,7 @@ PieceTable::Insert(std::size_t byte_offset, const char *text, std::size_t len)
|
||||
pieces_.push_back(Piece{Source::Add, add_start, len});
|
||||
total_size_ += len;
|
||||
dirty_ = true;
|
||||
InvalidateLineIndex();
|
||||
lineIndexOnInsert(byte_offset, add_.data() + add_start, len);
|
||||
maybeConsolidate();
|
||||
version_++;
|
||||
range_cache_ = {};
|
||||
@@ -419,7 +456,7 @@ PieceTable::Insert(std::size_t byte_offset, const char *text, std::size_t len)
|
||||
pieces_.push_back(Piece{Source::Add, add_start, len});
|
||||
total_size_ += len;
|
||||
dirty_ = true;
|
||||
InvalidateLineIndex();
|
||||
lineIndexOnInsert(byte_offset, add_.data() + add_start, len);
|
||||
coalesceNeighbors(pieces_.size() - 1);
|
||||
maybeConsolidate();
|
||||
version_++;
|
||||
@@ -447,7 +484,7 @@ PieceTable::Insert(std::size_t byte_offset, const char *text, std::size_t len)
|
||||
|
||||
total_size_ += len;
|
||||
dirty_ = true;
|
||||
InvalidateLineIndex();
|
||||
lineIndexOnInsert(byte_offset, add_.data() + add_start, len);
|
||||
// Try coalescing around the inserted position (the inserted piece is at idx + (inner>0 ? 1 : 0))
|
||||
std::size_t ins_index = idx + (inner > 0 ? 1 : 0);
|
||||
coalesceNeighbors(ins_index);
|
||||
@@ -517,7 +554,7 @@ PieceTable::Delete(std::size_t byte_offset, std::size_t len)
|
||||
|
||||
total_size_ -= len;
|
||||
dirty_ = true;
|
||||
InvalidateLineIndex();
|
||||
lineIndexOnDelete(byte_offset, len);
|
||||
if (idx < pieces_.size())
|
||||
coalesceNeighbors(idx);
|
||||
if (idx > 0)
|
||||
|
||||
Reference in New Issue
Block a user