1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 03:37:43 +00:00

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.
This commit is contained in:
Max Wipfli 2021-06-03 22:33:53 +02:00 committed by Andreas Kling
parent 9c0cfede59
commit 054c742d17

View file

@ -103,35 +103,63 @@ void TextNode::paint_cursor_if_needed(PaintContext& context, const LineBoxFragme
context.painter().draw_rect(cursor_rect, computed_values().color()); 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) void TextNode::compute_text_for_rendering(bool collapse, bool previous_is_empty_or_ends_in_whitespace)
{ {
if (!collapse) { auto& data = dom_node().data();
m_text_for_rendering = dom_node().data(); if (!collapse || data.is_empty()) {
m_text_for_rendering = data;
return; return;
} }
// Collapse whitespace into single spaces // NOTE: A couple fast returns to avoid unnecessarily allocating a StringBuilder.
auto utf8_view = Utf8View(dom_node().data()); if (data.length() == 1) {
StringBuilder builder(dom_node().data().length()); if (is_ascii_space(data[0])) {
auto it = utf8_view.begin(); if (previous_is_empty_or_ends_in_whitespace)
auto skip_over_whitespace = [&] { m_text_for_rendering = String::empty();
auto prev = it; else {
while (it != utf8_view.end() && is_ascii_space(*it)) { static String s_single_space_string = " ";
prev = it; m_text_for_rendering = s_single_space_string;
++it; }
}
it = prev;
};
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 { } else {
builder.append(' '); m_text_for_rendering = data;
skip_over_whitespace(); }
return;
}
bool contains_space = false;
for (auto& c : data) {
if (is_ascii_space(c)) {
contains_space = true;
break;
} }
} }
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();
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(); m_text_for_rendering = builder.to_string();
} }