diff options
author | Max Wipfli <mail@maxwipfli.ch> | 2021-06-03 22:33:53 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-06-04 09:39:07 +0200 |
commit | 054c742d177452d7516ca26a7bed72f39b6365f0 (patch) | |
tree | eb7d62e3bc7e88bdb9c49a95bdd072b364568c86 /Userland | |
parent | 9c0cfede593d41549095df7252429247abc34bb4 (diff) | |
download | serenity-054c742d177452d7516ca26a7bed72f39b6365f0.zip |
LibWeb: Remove Utf8View usage and try avoiding StringBuilder in TextNode
This patch completely reworks TextNode::compute_text_for_rendering(). It
removes the unnecessary usage of Utf8View to find spaces in a String.
Furthermore, it adds a couple fast return paths for common but trivial
cases such as empty, single-character and whitespace-less strings.
For the HTML spec bookmarks, around two thirds of all function calls
(which amounts to around 10'000) use the fast paths and thus avoid
allocating a StringBuilder just to build a copy of the already present
String.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/TextNode.cpp | 60 |
1 files changed, 44 insertions, 16 deletions
diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.cpp b/Userland/Libraries/LibWeb/Layout/TextNode.cpp index 1520788d0c..e03097e0d9 100644 --- a/Userland/Libraries/LibWeb/Layout/TextNode.cpp +++ b/Userland/Libraries/LibWeb/Layout/TextNode.cpp @@ -103,35 +103,63 @@ void TextNode::paint_cursor_if_needed(PaintContext& context, const LineBoxFragme context.painter().draw_rect(cursor_rect, computed_values().color()); } +// NOTE: This collapes whitespace into a single ASCII space if collapse is true. If previous_is_empty_or_ends_in_whitespace, it also strips leading whitespace. void TextNode::compute_text_for_rendering(bool collapse, bool previous_is_empty_or_ends_in_whitespace) { - if (!collapse) { - m_text_for_rendering = dom_node().data(); + auto& data = dom_node().data(); + if (!collapse || data.is_empty()) { + m_text_for_rendering = data; return; } - // Collapse whitespace into single spaces - auto utf8_view = Utf8View(dom_node().data()); - StringBuilder builder(dom_node().data().length()); - auto it = utf8_view.begin(); - auto skip_over_whitespace = [&] { - auto prev = it; - while (it != utf8_view.end() && is_ascii_space(*it)) { - prev = it; - ++it; + // NOTE: A couple fast returns to avoid unnecessarily allocating a StringBuilder. + if (data.length() == 1) { + if (is_ascii_space(data[0])) { + if (previous_is_empty_or_ends_in_whitespace) + m_text_for_rendering = String::empty(); + else { + static String s_single_space_string = " "; + m_text_for_rendering = s_single_space_string; + } + } else { + m_text_for_rendering = data; + } + return; + } + + bool contains_space = false; + for (auto& c : data) { + if (is_ascii_space(c)) { + contains_space = true; + break; } - it = prev; + } + if (!contains_space) { + m_text_for_rendering = data; + return; + } + + StringBuilder builder(data.length()); + size_t index = 0; + + auto skip_over_whitespace = [&index, &data] { + while (index < data.length() && is_ascii_space(data[index])) + ++index; }; + if (previous_is_empty_or_ends_in_whitespace) skip_over_whitespace(); - for (; it != utf8_view.end(); ++it) { - if (!is_ascii_space(*it)) { - builder.append(StringView { it.underlying_code_point_bytes() }); - } else { + while (index < data.length()) { + if (is_ascii_space(data[index])) { builder.append(' '); + ++index; skip_over_whitespace(); + } else { + builder.append(data[index]); + ++index; } } + m_text_for_rendering = builder.to_string(); } |