1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 06:17:35 +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());
}
// 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;
}
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() });
// 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 {
builder.append(' ');
skip_over_whitespace();
m_text_for_rendering = data;
}
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();
}