diff options
| author | Maxime Coste <mawww@kakoune.org> | 2023-12-26 20:55:47 +1100 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2023-12-26 21:21:01 +1100 |
| commit | 43e8aadcaad0036b6910d1a730e4f6c4c1ebd72c (patch) | |
| tree | e198b77f414649a2d53f2feb4a85344672d4cf62 /src/buffer.cc | |
| parent | 68e73d8a2404b4570567a524f75f3fde141b64ab (diff) | |
Fix performance of diff-based reloading of buffers
It turns out diffing was pretty fast, but applying the diff was
sub-optimal as it was constantly inserting/erasing lines which
led to lots of unnecessary shifting. Fix this by manually tracking
a read/write iterator and only shifting when necessary (on keeps,
and inserts).
Diffstat (limited to 'src/buffer.cc')
| -rw-r--r-- | src/buffer.cc | 37 |
1 files changed, 26 insertions, 11 deletions
diff --git a/src/buffer.cc b/src/buffer.cc index 4460bab7..b9335322 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -219,40 +219,55 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F [](const StringDataPtr& lhs, const StringDataPtr& rhs) { return lhs->strview() == rhs->strview(); }); - auto it = m_lines.begin(); + auto read_it = m_lines.begin(); + auto write_it = m_lines.begin(); auto new_it = lines.begin(); - for (auto& [op, len] : diff) + for (auto [op, len] : diff) { + kak_assert(read_it >= write_it); if (op == DiffOp::Keep) { - it += len; + if (read_it != write_it) + std::move(read_it, read_it + len, write_it); + write_it += len; + read_it += len; new_it += len; } else if (op == DiffOp::Add) { - const LineCount cur_line = (int)(it - m_lines.begin()); - + const LineCount cur_line = (int)(write_it - m_lines.begin()); for (LineCount line = 0; line < len; ++line) m_current_undo_group.push_back({Modification::Insert, cur_line + line, *(new_it + (int)line)}); - m_changes.push_back({Change::Insert, cur_line, cur_line + len}); - m_lines.insert(it, new_it, new_it + len); - it = m_lines.begin() + (int)(cur_line + len); + + if (read_it != write_it) + { + auto count = std::min(len, static_cast<int>(read_it - write_it)); + write_it = std::copy(new_it, new_it + count, write_it); + new_it += count; + if (len == count) + continue; + len -= count; + } + + auto read_pos = read_it - m_lines.begin(); + write_it = m_lines.insert(write_it, new_it, new_it + len) + len; + read_it = m_lines.begin() + read_pos + len; new_it += len; } else if (op == DiffOp::Remove) { - const LineCount cur_line = (int)(it - m_lines.begin()); - + const LineCount cur_line = (int)(write_it - m_lines.begin()); for (LineCount line = len-1; line >= 0; --line) m_current_undo_group.push_back({ Modification::Erase, cur_line + line, m_lines.get_storage(cur_line + line)}); - it = m_lines.erase(it, it + len); + read_it += len; m_changes.push_back({ Change::Erase, cur_line, cur_line + len }); } } + m_lines.erase(write_it, m_lines.end()); } commit_undo_group(); |
