From 93c50b3cd97ef78c678aa84010a5481ce0f11245 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 6 Dec 2022 17:51:28 +1100 Subject: [PATCH] Avoid calculating atom length in DisplayLine::trim_from Calculating the length of an atom means we need to decode every codepoint and compute its column width. This can prove quite expensive in trim_from as we can have full buffer lines, so on buffer with long lines we might have to go through megabytes of undisplayed data. --- src/display_buffer.cc | 73 ++++++++++++++++++++++++++++--------------- src/display_buffer.hh | 5 +-- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/display_buffer.cc b/src/display_buffer.cc index c9b74d04..02007d8f 100644 --- a/src/display_buffer.cc +++ b/src/display_buffer.cc @@ -55,24 +55,56 @@ ColumnCount DisplayAtom::length() const return 0; } -void DisplayAtom::trim_begin(ColumnCount count) +bool DisplayAtom::empty() const { if (m_type == Range) - m_range.begin = utf8::advance(get_iterator(*m_buffer, m_range.begin), - get_iterator(*m_buffer, m_range.end), - count).coord(); + return m_range.begin == m_range.end; else - m_text = m_text.substr(count).str(); + return m_text.empty(); } -void DisplayAtom::trim_end(ColumnCount count) +ColumnCount DisplayAtom::trim_begin(ColumnCount count) { + ColumnCount res = 0; if (m_type == Range) - m_range.end = utf8::advance(get_iterator(*m_buffer, m_range.end), - get_iterator(*m_buffer, m_range.begin), - -count).coord(); + { + auto it = get_iterator(*m_buffer, m_range.begin); + auto end = get_iterator(*m_buffer, m_range.end); + while (it != end and res < count) + res += codepoint_width(utf8::read_codepoint(it, end)); + m_range.begin = std::min(it.coord(), m_range.end); + } else - m_text = m_text.substr(0, m_text.column_length() - count).str(); + { + auto it = m_text.begin(); + while (it != m_text.end() and res < count) + res += codepoint_width(utf8::read_codepoint(it, m_text.end())); + m_text = String{it, m_text.end()}; + } + + return res; +} + +ColumnCount DisplayAtom::trim_end_to_length(ColumnCount count) +{ + ColumnCount res = 0; + if (m_type == Range) + { + auto it = get_iterator(*m_buffer, m_range.begin); + auto end = get_iterator(*m_buffer, m_range.end); + while (it != end and res < count) + res += codepoint_width(utf8::read_codepoint(it, end)); + m_range.end = std::min(it.coord(), m_range.end); + } + else + { + auto it = m_text.begin(); + while (it != m_text.end() and res < count) + res += codepoint_width(utf8::read_codepoint(it, m_text.end())); + m_text = String{m_text.begin(), it}; + } + + return res; } DisplayLine::DisplayLine(AtomList atoms) @@ -222,26 +254,15 @@ bool DisplayLine::trim_from(ColumnCount first_col, ColumnCount front, ColumnCoun while (front > 0 and it != end()) { - auto len = it->length(); - if (len <= front) - { - m_atoms.erase(it); - front -= len; - } - else - { - it->trim_begin(front); - front = 0; - } + front -= it->trim_begin(front); + if (it->empty()) + it = m_atoms.erase(it); } it = begin(); for (; it != end() and col_count > 0; ++it) - col_count -= it->length(); - - bool did_trim = it != end() || col_count < 0; - if (col_count < 0) - (it-1)->trim_end(-col_count); + col_count -= it->trim_end_to_length(col_count); + bool did_trim = it != end() && col_count == 0; m_atoms.erase(it, end()); compute_range(); diff --git a/src/display_buffer.hh b/src/display_buffer.hh index c164cb08..c81fc2f6 100644 --- a/src/display_buffer.hh +++ b/src/display_buffer.hh @@ -38,6 +38,7 @@ public: StringView content() const; ColumnCount length() const; + bool empty() const; const BufferCoord& begin() const { @@ -74,8 +75,8 @@ public: Type type() const { return m_type; } - void trim_begin(ColumnCount count); - void trim_end(ColumnCount count); + ColumnCount trim_begin(ColumnCount count); + ColumnCount trim_end_to_length(ColumnCount count); bool operator==(const DisplayAtom& other) const {